From 3cbb43a73a5504a4c4285394a4876eef202d7620 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 12 Nov 2012 23:31:32 +0100 Subject: [PATCH] Add basic TCRYPT library. --- configure.in | 1 + lib/Makefile.am | 6 +- lib/libcryptsetup.h | 22 +++ lib/setup.c | 53 ++++++ lib/tcrypt/Makefile.am | 16 ++ lib/tcrypt/tcrypt.c | 408 +++++++++++++++++++++++++++++++++++++++++ lib/tcrypt/tcrypt.h | 78 ++++++++ src/cryptsetup.c | 50 +++++ 8 files changed, 632 insertions(+), 2 deletions(-) create mode 100644 lib/tcrypt/Makefile.am create mode 100644 lib/tcrypt/tcrypt.c create mode 100644 lib/tcrypt/tcrypt.h diff --git a/configure.in b/configure.in index f04f5992..6b7a79b4 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/lib/Makefile.am b/lib/Makefile.am index 3ff70029..c1950002 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 443ba9f8..747cb0c4 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -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 */ +}; + /** @} */ /** diff --git a/lib/setup.c b/lib/setup.c index e47ae494..29c1f0fc 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -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 index 00000000..a7c1f7da --- /dev/null +++ b/lib/tcrypt/Makefile.am @@ -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 index 00000000..31b09165 --- /dev/null +++ b/lib/tcrypt/tcrypt.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 00000000..577221e1 --- /dev/null +++ b/lib/tcrypt/tcrypt.h @@ -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 diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 7a0289fe..b520d40f 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -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_(""), N_("Restore LUKS device header and keyslots") }, { "loopaesOpen",action_loopaesOpen, 0, 2, 1, N_(" "), N_("open loop-AES device as mapping ") }, { "loopaesClose",action_remove, 0, 1, 1, N_(""), N_("remove loop-AES mapping") }, + { "tcryptOpen", action_tcryptOpen, 0, 2, 1, N_(" "), N_("open TCRYPT device as mapping ") }, { 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**)¶ms.passphrase, + ¶ms.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, ¶ms); + 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);