Add basic TCRYPT library.
authorMilan Broz <gmazyland@gmail.com>
Mon, 12 Nov 2012 22:31:32 +0000 (23:31 +0100)
committerMilan Broz <gmazyland@gmail.com>
Mon, 19 Nov 2012 20:24:59 +0000 (21:24 +0100)
configure.in
lib/Makefile.am
lib/libcryptsetup.h
lib/setup.c
lib/tcrypt/Makefile.am [new file with mode: 0644]
lib/tcrypt/tcrypt.c [new file with mode: 0644]
lib/tcrypt/tcrypt.h [new file with mode: 0644]
src/cryptsetup.c

index f04f599..6b7a79b 100644 (file)
@@ -367,6 +367,7 @@ lib/crypto_backend/Makefile
 lib/luks1/Makefile
 lib/loopaes/Makefile
 lib/verity/Makefile
+lib/tcrypt/Makefile
 src/Makefile
 po/Makefile.in
 man/Makefile
index 3ff7002..c195000 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = crypto_backend luks1 loopaes verity
+SUBDIRS = crypto_backend luks1 loopaes verity tcrypt
 
 moduledir = $(libdir)/cryptsetup
 
@@ -11,6 +11,7 @@ INCLUDES = \
        -I$(top_srcdir)/lib/luks1               \
        -I$(top_srcdir)/lib/loopaes             \
        -I$(top_srcdir)/lib/verity              \
+       -I$(top_srcdir)/lib/tcrypt              \
        -DDATADIR=\""$(datadir)"\"              \
        -DLIBDIR=\""$(libdir)"\"                \
        -DPREFIX=\""$(prefix)"\"                \
@@ -26,7 +27,8 @@ common_ldadd = \
        crypto_backend/libcrypto_backend.la     \
        luks1/libluks1.la                       \
        loopaes/libloopaes.la                   \
-       verity/libverity.la
+       verity/libverity.la                     \
+       tcrypt/libtcrypt.la
 
 libcryptsetup_la_DEPENDENCIES = $(common_ldadd) libcryptsetup.sym
 
index 443ba9f..747cb0c 100644 (file)
@@ -302,6 +302,8 @@ int crypt_memory_lock(struct crypt_device *cd, int lock);
 #define CRYPT_LOOPAES "LOOPAES"
 /** dm-verity mode */
 #define CRYPT_VERITY "VERITY"
+/** TCRYPT mode */
+#define CRYPT_TCRYPT "TCRYPT"
 
 /**
  * Get device type
@@ -380,6 +382,26 @@ struct crypt_params_verity {
        uint32_t flags;            /**< CRYPT_VERITY* flags */
 };
 
+/**
+ *
+ * Structure used as parameter for TCRYPT device type
+ *
+ * @see crypt_format
+ *
+ */
+/** Try to load hidden header (describing hidden device) */
+#define CRYPT_TCRYPT_HIDDEN_HEADER   (1 << 0)
+
+struct crypt_params_tcrypt {
+       const char *passphrase;
+       size_t passphrase_size;
+       const char *hash_name;     /**< hash function for PBKDF */
+       const char *cipher[3];     /**< cipher chain */
+       const char *mode;          /**< cipher block mode */
+       size_t key_size;           /**< key size in bytes */
+       uint32_t flags;            /**< CRYPT_TCRYPT* flags */
+};
+
 /** @} */
 
 /**
index e47ae49..29c1f0f 100644 (file)
@@ -30,6 +30,7 @@
 #include "luks.h"
 #include "loopaes.h"
 #include "verity.h"
+#include "tcrypt.h"
 #include "internal.h"
 
 struct crypt_device {
@@ -69,6 +70,10 @@ struct crypt_device {
        unsigned int verity_root_hash_size;
        char *verity_uuid;
 
+       /* used in CRYPT_TCRYPT */
+       struct crypt_params_tcrypt tcrypt_params;
+       struct tcrypt_phdr tcrypt_hdr;
+
        /* callbacks definitions */
        void (*log)(int level, const char *msg, void *usrptr);
        void *log_usrptr;
@@ -243,6 +248,11 @@ static int isVERITY(const char *type)
        return (type && !strcmp(CRYPT_VERITY, type));
 }
 
+static int isTCRYPT(const char *type)
+{
+       return (type && !strcmp(CRYPT_TCRYPT, type));
+}
+
 /* keyslot helpers */
 static int keyslot_verify_or_find_empty(struct crypt_device *cd, int *keyslot)
 {
@@ -616,6 +626,26 @@ static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int re
        return r;
 }
 
+static int _crypt_load_tcrypt(struct crypt_device *cd, struct crypt_params_tcrypt *params)
+{
+       int r;
+
+       r = init_crypto(cd);
+       if (r < 0)
+               return r;
+
+       r = TCRYPT_read_phdr(cd, &cd->tcrypt_hdr, &cd->tcrypt_params,
+                            params->passphrase, params->passphrase_size,
+                            params->flags);
+       if (r < 0)
+               return r;
+
+       if (!cd->type && !(cd->type = strdup(CRYPT_TCRYPT)))
+               return -ENOMEM;
+
+       return r;
+}
+
 static int _crypt_load_verity(struct crypt_device *cd, struct crypt_params_verity *params)
 {
        int r;
@@ -1187,6 +1217,12 @@ int crypt_load(struct crypt_device *cd,
                        return -EINVAL;
                }
                r = _crypt_load_verity(cd, params);
+       } else if (isTCRYPT(requested_type)) {
+               if (cd->type && !isTCRYPT(cd->type)) {
+                       log_dbg("Context is already initialised to type %s", cd->type);
+                       return -EINVAL;
+               }
+               r = _crypt_load_tcrypt(cd, params);
        } else
                return -EINVAL;
 
@@ -2005,6 +2041,11 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
                        if (cd->verity_root_hash)
                                memcpy(cd->verity_root_hash, volume_key, volume_key_size);
                }
+       } else if (isTCRYPT(cd->type)) {
+               if (!name)
+                       return -EINVAL;
+               r = TCRYPT_activate(cd, name, &cd->tcrypt_hdr,
+                                   &cd->tcrypt_params, flags);
        } else
                log_err(cd, _("Device type is not properly initialised.\n"));
 
@@ -2359,6 +2400,12 @@ uint64_t crypt_get_data_offset(struct crypt_device *cd)
        if (isLOOPAES(cd->type))
                return cd->loopaes_hdr.offset;
 
+       if (isTCRYPT(cd->type)) { // FIXME: system vol.
+               if (!cd->tcrypt_hdr.d.mk_offset)
+                       return 1;
+               return (cd->tcrypt_hdr.d.mk_offset / cd->tcrypt_hdr.d.sector_size);
+       }
+
        return 0;
 }
 
@@ -2373,6 +2420,12 @@ uint64_t crypt_get_iv_offset(struct crypt_device *cd)
        if (isLOOPAES(cd->type))
                return cd->loopaes_hdr.skip;
 
+       if (isTCRYPT(cd->type)) {
+               if (!cd->tcrypt_hdr.d.mk_offset)
+                       return 0;
+               return (cd->tcrypt_hdr.d.mk_offset / cd->tcrypt_hdr.d.sector_size);
+       }
+
        return 0;
 }
 
diff --git a/lib/tcrypt/Makefile.am b/lib/tcrypt/Makefile.am
new file mode 100644 (file)
index 0000000..a7c1f7d
--- /dev/null
@@ -0,0 +1,16 @@
+moduledir = $(libdir)/cryptsetup
+
+noinst_LTLIBRARIES = libtcrypt.la
+
+libtcrypt_la_CFLAGS = -Wall @CRYPTO_CFLAGS@
+
+libtcrypt_la_SOURCES = \
+       tcrypt.c \
+       tcrypt.h
+
+INCLUDES = -D_GNU_SOURCE                       \
+       -D_LARGEFILE64_SOURCE                   \
+       -D_FILE_OFFSET_BITS=64                  \
+        -I$(top_srcdir)/lib                    \
+        -I$(top_srcdir)/lib/crypto_backend
+
diff --git a/lib/tcrypt/tcrypt.c b/lib/tcrypt/tcrypt.c
new file mode 100644 (file)
index 0000000..31b0916
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * TCRYPT compatible volume handling
+ *
+ * Copyright (C) 2012, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012, Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "libcryptsetup.h"
+#include "tcrypt.h"
+#include "internal.h"
+
+/* TCRYPT PBKDF variants */
+static struct {
+       char *name;
+       char *hash;
+       unsigned int iterations;
+} tcrypt_kdf[] = {
+       { "pbkdf2", "ripemd160", 2000 },
+       { "pbkdf2", "ripemd160", 1000 },
+       { "pbkdf2", "sha512",    1000 },
+       { "pbkdf2", "whirlpool", 1000 },
+       { NULL,     NULL,           0 }
+};
+
+/* TCRYPT cipher variants */
+static struct {
+       const char *cipher[3];
+       const char *mode;
+       int key_size;
+} tcrypt_cipher[] = {
+       { { "aes",     NULL,      NULL      }, "xts-plain64", 64 },
+       { { "twofish", NULL,      NULL      }, "xts-plain64", 64 },
+       { { "serpent", NULL,      NULL      }, "xts-plain64", 64 },
+       { { "aes",     "twofish", "serpent" }, "xts-plain64", 64 },
+       { { "serpent", "twofish", "aes"     }, "xts-plain64", 64 },
+       { { "twofish", "aes",     NULL      }, "xts-plain64", 64 },
+       { { "aes",     "serpent", NULL      }, "xts-plain64", 64 },
+       { { "serpent", "twofish", NULL      }, "xts-plain64", 64 },
+       { { "aes",     NULL,      NULL      }, "lrw-benbi",   48 },
+       { { "twofish", NULL,      NULL      }, "lrw-benbi",   48 },
+       { { "serpent", NULL,      NULL      }, "lrw-benbi",   48 },
+       { { "aes",     "twofish", "serpent" }, "lrw-benbi",   48 },
+       { { "serpent", "twofish", "aes"     }, "lrw-benbi",   48 },
+       { { "twofish", "aes",     NULL      }, "lrw-benbi",   48 },
+       { { "aes",     "serpent", NULL      }, "lrw-benbi",   48 },
+       { { "serpent", "twofish", NULL      }, "lrw-benbi",   48 },
+       { { NULL,      NULL,      NULL      }, NULL,           0 }
+};
+
+static void hdr_info(struct crypt_device *cd, struct tcrypt_phdr *hdr,
+                    struct crypt_params_tcrypt *params)
+{
+       log_dbg("Version: %d, required %d", (int)hdr->d.version, (int)hdr->d.version_tc);
+
+       log_dbg("Hidden size: %" PRIu64, hdr->d.hidden_volume_size);
+       log_dbg("Volume size: %" PRIu64, hdr->d.volume_size);
+
+       log_dbg("Sector size: %" PRIu64, hdr->d.sector_size);
+       log_dbg("Flags: %d", (int)hdr->d.flags);
+       log_dbg("MK: offset %d, size %d", (int)hdr->d.mk_offset, (int)hdr->d.mk_size);
+       log_dbg("KDF: PBKDF2, hash %s", params->hash_name);
+       log_dbg("Cipher: %s%s%s%s%s-%s",
+               params->cipher[0],
+               params->cipher[1] ? "-" : "", params->cipher[1] ?: "",
+               params->cipher[2] ? "-" : "", params->cipher[2] ?: "",
+               params->mode);
+}
+
+static int hdr_from_disk(struct tcrypt_phdr *hdr,
+                        struct crypt_params_tcrypt *params,
+                        int kdf_index, int cipher_index)
+{
+       uint32_t crc32;
+       size_t size;
+
+       /* Check CRC32 of header */
+       size = TCRYPT_HDR_LEN - sizeof(hdr->d.keys) - sizeof(hdr->d.header_crc32);
+       crc32 = crypt_crc32(~0, (unsigned char*)&hdr->d, size) ^ ~0;
+       if (be16_to_cpu(hdr->d.version) > 3 &&
+           crc32 != be32_to_cpu(hdr->d.header_crc32)) {
+               log_dbg("TCRYPT header CRC32 mismatch.");
+               return -EINVAL;
+       }
+
+       /* Check CRC32 of keys */
+       crc32 = crypt_crc32(~0, (unsigned char*)hdr->d.keys, sizeof(hdr->d.keys)) ^ ~0;
+       if (crc32 != be32_to_cpu(hdr->d.keys_crc32)) {
+               log_dbg("TCRYPT keys CRC32 mismatch.");
+               return -EINVAL;
+       }
+
+       /* Convert header to cpu format */
+       hdr->d.version  =  be16_to_cpu(hdr->d.version);
+       hdr->d.version_tc = le16_to_cpu(hdr->d.version_tc); // ???
+
+       hdr->d.keys_crc32 = be32_to_cpu(hdr->d.keys_crc32);
+
+       hdr->d.hidden_volume_size = be64_to_cpu(hdr->d.hidden_volume_size);
+       hdr->d.volume_size        = be64_to_cpu(hdr->d.volume_size);
+
+       hdr->d.mk_offset = be64_to_cpu(hdr->d.mk_offset);
+       if (!hdr->d.mk_offset)
+               hdr->d.mk_offset = 512;
+
+       hdr->d.mk_size = be64_to_cpu(hdr->d.mk_size);
+
+       hdr->d.flags = be32_to_cpu(hdr->d.flags);
+
+       hdr->d.sector_size = be32_to_cpu(hdr->d.sector_size);
+       if (!hdr->d.sector_size)
+               hdr->d.sector_size = 512;
+
+       hdr->d.header_crc32 = be32_to_cpu(hdr->d.header_crc32);
+
+       /* Set params */
+       params->passphrase = NULL;
+       params->passphrase_size = 0;
+
+       params->hash_name  = tcrypt_kdf[kdf_index].hash;
+
+       params->cipher[0]  = tcrypt_cipher[cipher_index].cipher[0];
+       params->cipher[1]  = tcrypt_cipher[cipher_index].cipher[1];
+       params->cipher[2]  = tcrypt_cipher[cipher_index].cipher[2];
+       params->mode     = tcrypt_cipher[cipher_index].mode;
+       params->key_size = tcrypt_cipher[cipher_index].key_size;
+
+       return 0;
+}
+
+static int decrypt_hdr_one(const char *name, const char *mode,
+                          const char *key, size_t key_size,
+                          struct tcrypt_phdr *hdr)
+{
+       char iv[TCRYPT_HDR_IV_LEN] = {};
+       char mode_name[MAX_CIPHER_LEN];
+       struct crypt_cipher *cipher;
+       void *buf = &hdr->e;
+       char *c;
+       int r;
+
+       /* Remove IV if present */
+       strncpy(mode_name, mode, MAX_CIPHER_LEN);
+       c = strchr(mode_name, '-');
+       if (c)
+               *c = '\0';
+
+       if (!strncmp(mode, "lrw", 3))
+               iv[15] = 1;
+
+       r = crypt_cipher_init(&cipher, name, mode_name, key, key_size);
+       if (r < 0)
+               return r;
+
+       r = crypt_cipher_decrypt(cipher, buf, buf, TCRYPT_HDR_LEN,
+                                iv, TCRYPT_HDR_IV_LEN);
+       crypt_cipher_destroy(cipher);
+
+       return r;
+}
+
+static void copy_key(char *out_key, const char *key, int key_num,
+                    int ks, int ki, const char *mode)
+{
+       if (!strncmp(mode, "xts", 3)) {
+               int ks2 = ks / 2;
+               memcpy(out_key, &key[ks2 * ki], ks2);
+               memcpy(&out_key[ks2], &key[ks2 * (++key_num + ki)], ks2);
+       } else if (!strncmp(mode, "lrw", 3)) {
+               /* First is LRW index key */
+               ki++;
+               ks -= TCRYPT_LRW_IKEY_LEN;
+               memcpy(out_key, &key[ks * ki], ks);
+               memcpy(&out_key[ks * ki], key, TCRYPT_LRW_IKEY_LEN);
+       }
+}
+
+static int top_cipher(const char *cipher[3])
+{
+       if (cipher[2])
+               return 2;
+
+       if (cipher[1])
+               return 1;
+
+       return 0;
+}
+
+static int decrypt_hdr(struct crypt_device *cd, struct tcrypt_phdr *hdr,
+                       const char *key)
+{
+       char one_key[TCRYPT_HDR_KEY_LEN];
+       struct tcrypt_phdr hdr2;
+       int i, j, r;
+
+       for (i = 0; tcrypt_cipher[i].cipher[0]; i++) {
+               log_dbg("TCRYPT:  trying cipher: %s%s%s%s%s-%s.",
+                       tcrypt_cipher[i].cipher[0],
+                       tcrypt_cipher[i].cipher[1] ? "-" : "", tcrypt_cipher[i].cipher[1] ?: "",
+                       tcrypt_cipher[i].cipher[2] ? "-" : "", tcrypt_cipher[i].cipher[2] ?: "",
+                       tcrypt_cipher[i].mode);
+
+               memcpy(&hdr2.e, &hdr->e, TCRYPT_HDR_LEN);
+
+               for (j = 2; j >= 0 ; j--) {
+                       if (!tcrypt_cipher[i].cipher[j])
+                               continue;
+                       copy_key(one_key, key, top_cipher(tcrypt_cipher[i].cipher),
+                                tcrypt_cipher[i].key_size,
+                                j, tcrypt_cipher[i].mode);
+                       r = decrypt_hdr_one(tcrypt_cipher[i].cipher[j],
+                                           tcrypt_cipher[i].mode, one_key,
+                                           tcrypt_cipher[i].key_size, &hdr2);
+                       if (r < 0)
+                               break;
+               }
+
+               if (!strncmp(hdr2.d.magic, TCRYPT_HDR_MAGIC, TCRYPT_HDR_MAGIC_LEN)) {
+                       log_dbg("TCRYPT: Signature magic detected.");
+                       memcpy(&hdr->e, &hdr2.e, TCRYPT_HDR_LEN);
+                       memset(&hdr2.e, 0, TCRYPT_HDR_LEN);
+                       r = i;
+                       break;
+               }
+               r = -EPERM;
+       }
+
+       memset(one_key, 0, sizeof(*one_key));
+       return r;
+}
+
+static int TCRYPT_init_hdr(struct crypt_device *cd,
+                          struct tcrypt_phdr *hdr,
+                          struct crypt_params_tcrypt *params,
+                          const char *passphrase,
+                          size_t passphrase_size)
+{
+       char *key;
+       int r, i;
+
+       if (posix_memalign((void*)&key, crypt_getpagesize(), TCRYPT_HDR_KEY_LEN))
+               return -ENOMEM;
+
+       for (i = 0; tcrypt_kdf[i].name; i++) {
+               /* Derive header key */
+               log_dbg("TCRYPT: trying KDF: %s-%s-%d.",
+                       tcrypt_kdf[i].name, tcrypt_kdf[i].hash, tcrypt_kdf[i].iterations);
+               r = crypt_pbkdf(tcrypt_kdf[i].name, tcrypt_kdf[i].hash,
+                               passphrase, passphrase_size,
+                               hdr->salt, TCRYPT_HDR_SALT_LEN,
+                               key, TCRYPT_HDR_KEY_LEN,
+                               tcrypt_kdf[i].iterations);
+               if (r < 0)
+                       break;
+
+               /* Decrypt header */
+               r = decrypt_hdr(cd, hdr, key);
+               if (r != -EPERM)
+                       break;
+       }
+       free(key);
+
+       if (r < 0)
+               return r;
+
+       r = hdr_from_disk(hdr, params, i, r);
+       if (r < 0)
+               return r;
+
+       hdr_info(cd, hdr, params);
+       return 0;
+}
+
+int TCRYPT_read_phdr(struct crypt_device *cd,
+                    struct tcrypt_phdr *hdr,
+                    struct crypt_params_tcrypt *params,
+                    const char *passphrase,
+                    size_t passphrase_size,
+                    uint32_t flags)
+{
+       struct device *device = crypt_metadata_device(cd);
+       ssize_t hdr_size = sizeof(struct tcrypt_phdr);
+       int devfd = 0, r;
+
+       assert(sizeof(struct tcrypt_phdr) == 512);
+
+       log_dbg("Reading TCRYPT header of size %d bytes from device %s.",
+               hdr_size, device_path(device));
+
+       devfd = open(device_path(device), O_RDONLY | O_DIRECT);
+       if (devfd == -1) {
+               log_err(cd, _("Cannot open device %s.\n"), device_path(device));
+               return -EINVAL;
+       }
+
+       if ((flags & CRYPT_TCRYPT_HIDDEN_HEADER) &&
+           lseek(devfd, TCRYPT_HDR_HIDDEN_OFFSET, SEEK_SET) < 0) {
+               log_err(cd, _("Cannot seek to hidden header for %s.\n"), device_path(device));
+               r = -EIO;
+               goto out;
+       }
+
+       if (read_blockwise(devfd, device_block_size(device), hdr, hdr_size) == hdr_size) {
+               params->flags = flags;
+               r = TCRYPT_init_hdr(cd, hdr, params, passphrase, passphrase_size);
+       } else
+               r = -EIO;
+out:
+       close(devfd);
+       return r;
+}
+
+int TCRYPT_activate(struct crypt_device *cd,
+                    const char *name,
+                    struct tcrypt_phdr *hdr,
+                    struct crypt_params_tcrypt *params,
+                    uint32_t flags)
+{
+       char cipher[MAX_CIPHER_LEN], dm_name[PATH_MAX], dm_dev_name[PATH_MAX];
+       struct device *device = NULL;
+       int i, r;
+       struct crypt_dm_active_device dmd = {
+               .target = DM_CRYPT,
+               .size   = 0,
+               .data_device = crypt_data_device(cd),
+               .u.crypt  = {
+                       .cipher = cipher,
+                       .offset = crypt_get_data_offset(cd),
+                       .iv_offset = crypt_get_iv_offset(cd),
+               }
+       };
+
+       r = device_block_adjust(cd, dmd.data_device, DEV_EXCL,
+                               dmd.u.crypt.offset, &dmd.size, &dmd.flags);
+       if (r)
+               return r;
+
+       dmd.u.crypt.vk = crypt_alloc_volume_key(params->key_size, NULL);
+       if (!dmd.u.crypt.vk)
+               return -ENOMEM;
+
+       for (i = 2; i >= 0; i--) {
+
+               if (!params->cipher[i])
+                       continue;
+
+               if (i == 0) {
+                       strncpy(dm_name, name, sizeof(dm_name));
+                       dmd.flags = flags;
+               } else {
+                       snprintf(dm_name, sizeof(dm_name), "%s_%d", name, i);
+                       dmd.flags = flags | CRYPT_ACTIVATE_PRIVATE;
+               }
+
+               snprintf(cipher, sizeof(cipher), "%s-%s",
+                        params->cipher[i], params->mode);
+               copy_key(dmd.u.crypt.vk->key, hdr->d.keys,
+                        top_cipher(params->cipher),
+                        params->key_size, i, params->mode);
+
+               if (top_cipher(params->cipher) != i) {
+                       snprintf(dm_dev_name, sizeof(dm_dev_name), "%s/%s_%d",
+                                dm_get_dir(), name, i + 1);
+                       r = device_alloc(&device, dm_dev_name);
+                       if (r)
+                               break;
+                       dmd.data_device = device;
+                       dmd.u.crypt.offset = 0;
+               }
+
+               log_dbg("Trying to activate TCRYPT device %s using cipher %s.",
+                       dm_name, dmd.u.crypt.cipher);
+               r = dm_create_device(cd, dm_name, CRYPT_TCRYPT, &dmd, 0);
+
+               device_free(device);
+               device = NULL;
+
+               if (r)
+                       break;
+       }
+
+       if (!r && !(dm_flags() & DM_PLAIN64_SUPPORTED)) {
+               log_err(cd, _("Kernel doesn't support plain64 IV.\n"));
+               r = -ENOTSUP;
+       }
+
+       crypt_free_volume_key(dmd.u.crypt.vk);
+       return r;
+}
diff --git a/lib/tcrypt/tcrypt.h b/lib/tcrypt/tcrypt.h
new file mode 100644 (file)
index 0000000..577221e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * TCRYPT - header defitinion
+ *
+ * Copyright (C) 2012, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012, Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "libcryptsetup.h"
+
+#ifndef _CRYPTSETUP_TCRYPT_H
+#define _CRYPTSETUP_TCRYPT_H
+
+#define TCRYPT_HDR_SALT_LEN 64
+#define TCRYPT_HDR_IV_LEN   16
+#define TCRYPT_HDR_LEN     448
+#define TCRYPT_HDR_KEY_LEN 192
+#define TCRYPT_HDR_MAGIC "TRUE"
+#define TCRYPT_HDR_MAGIC_LEN 4
+
+#define TCRYPT_HDR_HIDDEN_OFFSET 65536
+
+#define TCRYPT_LRW_IKEY_LEN 16
+
+#define TCRYPT_HDR_FLAG_SYSTEM    (1 << 0)
+#define TCRYPT_HDR_FLAG_NONSYSTEM (1 << 1)
+
+struct tcrypt_phdr {
+       char salt[TCRYPT_HDR_SALT_LEN];
+
+       /* encrypted part, TCRYPT_HDR_LEN bytes */
+       union {
+       struct __attribute__((__packed__)) {
+               char     magic[TCRYPT_HDR_MAGIC_LEN];
+               uint16_t version;
+               uint16_t version_tc;
+               uint32_t keys_crc32;
+               uint64_t _reserved1[2]; /* data/header ctime */
+               uint64_t hidden_volume_size;
+               uint64_t volume_size;
+               uint64_t mk_offset;
+               uint64_t mk_size;
+               uint32_t flags;
+               uint32_t sector_size;
+               uint8_t  _reserved2[120];
+               uint32_t header_crc32;
+               char     keys[256];
+       } d;
+       char e[TCRYPT_HDR_LEN];
+       };
+} __attribute__((__packed__));
+
+int TCRYPT_read_phdr(struct crypt_device *cd,
+                    struct tcrypt_phdr *hdr,
+                    struct crypt_params_tcrypt *params,
+                    const char *passphrase,
+                    size_t passphrase_size,
+                    uint32_t flags);
+
+int TCRYPT_activate(struct crypt_device *cd,
+                    const char *name,
+                    struct tcrypt_phdr *hdr,
+                    struct crypt_params_tcrypt *params,
+                    uint32_t flags);
+
+#endif
index 7a0289f..b520d40 100644 (file)
@@ -51,6 +51,7 @@ static int opt_dump_master_key = 0;
 static int opt_shared = 0;
 static int opt_allow_discards = 0;
 static int opt_test_passphrase = 0;
+static int opt_hidden = 0;
 
 static const char **action_argv;
 static int action_argc;
@@ -76,6 +77,7 @@ static int action_luksBackup(int arg);
 static int action_luksRestore(int arg);
 static int action_loopaesOpen(int arg);
 static int action_luksRepair(int arg);
+static int action_tcryptOpen(int arg);
 
 static struct action_type {
        const char *type;
@@ -108,6 +110,7 @@ static struct action_type {
        { "luksHeaderRestore",action_luksRestore,0,1, 1, N_("<device>"), N_("Restore LUKS device header and keyslots") },
        { "loopaesOpen",action_loopaesOpen,     0, 2, 1, N_("<device> <name> "), N_("open loop-AES device as mapping <name>") },
        { "loopaesClose",action_remove,         0, 1, 1, N_("<name>"), N_("remove loop-AES mapping") },
+       { "tcryptOpen", action_tcryptOpen,      0, 2, 1, N_("<device> <name> "), N_("open TCRYPT device as mapping <name>") },
        { NULL, NULL, 0, 0, 0, NULL, NULL }
 };
 
@@ -252,6 +255,47 @@ out:
        return r;
 }
 
+static int action_tcryptOpen(int arg __attribute__((unused)))
+{
+       struct crypt_device *cd = NULL;
+       struct crypt_params_tcrypt params = {};
+       const char *activated_name;
+       uint32_t flags = 0;
+       int r;
+
+       activated_name = opt_test_passphrase ? NULL : action_argv[1];
+
+       if ((r = crypt_init(&cd, action_argv[0])))
+               goto out;
+
+       /* TCRYPT header is encrypted, get passphrase now */
+       r = crypt_get_key(_("Enter passphrase: "),
+                         CONST_CAST(char**)&params.passphrase,
+                         &params.passphrase_size,
+                         opt_keyfile_offset, opt_keyfile_size,
+                         NULL, opt_timeout,
+                         _verify_passphrase(0),
+                         cd);
+       if (r < 0)
+               goto out;
+
+       if (opt_hidden)
+               params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
+
+       r = crypt_load(cd, CRYPT_TCRYPT, &params);
+       if (r < 0)
+               goto out;
+
+       if (opt_readonly)
+               flags |= CRYPT_ACTIVATE_READONLY;
+
+       r = crypt_activate_by_volume_key(cd, activated_name, NULL, 0, flags);
+out:
+       crypt_free(cd);
+       crypt_safe_free(CONST_CAST(char*)params.passphrase);
+       return r;
+}
+
 static int action_remove(int arg __attribute__((unused)))
 {
        struct crypt_device *cd = NULL;
@@ -1198,6 +1242,7 @@ int main(int argc, const char **argv)
                { "allow-discards",    '\0', POPT_ARG_NONE, &opt_allow_discards,        0, N_("Allow discards (aka TRIM) requests for device."), NULL },
                { "header",            '\0', POPT_ARG_STRING, &opt_header_device,       0, N_("Device or file with separated LUKS header."), NULL },
                { "test-passphrase",   '\0', POPT_ARG_NONE, &opt_test_passphrase,       0, N_("Do not activate device, just check passphrase."), NULL },
+               { "hidden",            '\0', POPT_ARG_NONE, &opt_hidden,               0, N_("Use hidden header (hiden TCRYPT device) ."), NULL },
                POPT_TABLEEND
        };
        poptContext popt_context;
@@ -1365,6 +1410,11 @@ int main(int argc, const char **argv)
                _("Option --offset is supported only for create and loopaesOpen commands.\n"),
                poptGetInvocationName(popt_context));
 
+       if (opt_hidden && strcmp(aname, "tcryptOpen"))
+               usage(popt_context, EXIT_FAILURE,
+               _("Option --hidden is supported only for tcryptOpen command.\n"),
+               poptGetInvocationName(popt_context));
+
        if (opt_debug) {
                opt_verbose = 1;
                crypt_set_debug_level(-1);