aboutsummaryrefslogtreecommitdiff
path: root/crypto/block-luks.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/block-luks.c')
-rw-r--r--crypto/block-luks.c1696
1 files changed, 1113 insertions, 583 deletions
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 5738124773..3ee928fb5a 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,6 +23,7 @@
#include "qemu/bswap.h"
#include "block-luks.h"
+#include "block-luks-priv.h"
#include "crypto/hash.h"
#include "crypto/afsplit.h"
@@ -31,7 +32,7 @@
#include "crypto/random.h"
#include "qemu/uuid.h"
-#include "qemu/coroutine.h"
+#include "qemu/bitmap.h"
/*
* Reference for the LUKS format implemented here is
@@ -45,34 +46,6 @@
*/
typedef struct QCryptoBlockLUKS QCryptoBlockLUKS;
-typedef struct QCryptoBlockLUKSHeader QCryptoBlockLUKSHeader;
-typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
-
-
-/* The following constants are all defined by the LUKS spec */
-#define QCRYPTO_BLOCK_LUKS_VERSION 1
-
-#define QCRYPTO_BLOCK_LUKS_MAGIC_LEN 6
-#define QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN 32
-#define QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN 32
-#define QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN 32
-#define QCRYPTO_BLOCK_LUKS_DIGEST_LEN 20
-#define QCRYPTO_BLOCK_LUKS_SALT_LEN 32
-#define QCRYPTO_BLOCK_LUKS_UUID_LEN 40
-#define QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS 8
-#define QCRYPTO_BLOCK_LUKS_STRIPES 4000
-#define QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS 1000
-#define QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS 1000
-#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET 4096
-
-#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED 0x0000DEAD
-#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED 0x00AC71F3
-
-#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
-
-static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
- 'L', 'U', 'K', 'S', 0xBA, 0xBE
-};
typedef struct QCryptoBlockLUKSNameMap QCryptoBlockLUKSNameMap;
struct QCryptoBlockLUKSNameMap {
@@ -122,91 +95,55 @@ qcrypto_block_luks_cipher_size_map_twofish[] = {
{ 0, 0 },
};
+#ifdef CONFIG_CRYPTO_SM4
+static const QCryptoBlockLUKSCipherSizeMap
+qcrypto_block_luks_cipher_size_map_sm4[] = {
+ { 16, QCRYPTO_CIPHER_ALG_SM4},
+ { 0, 0 },
+};
+#endif
+
static const QCryptoBlockLUKSCipherNameMap
qcrypto_block_luks_cipher_name_map[] = {
{ "aes", qcrypto_block_luks_cipher_size_map_aes },
{ "cast5", qcrypto_block_luks_cipher_size_map_cast5 },
{ "serpent", qcrypto_block_luks_cipher_size_map_serpent },
{ "twofish", qcrypto_block_luks_cipher_size_map_twofish },
+#ifdef CONFIG_CRYPTO_SM4
+ { "sm4", qcrypto_block_luks_cipher_size_map_sm4},
+#endif
};
-
-/*
- * This struct is written to disk in big-endian format,
- * but operated upon in native-endian format.
- */
-struct QCryptoBlockLUKSKeySlot {
- /* state of keyslot, enabled/disable */
- uint32_t active;
- /* iterations for PBKDF2 */
- uint32_t iterations;
- /* salt for PBKDF2 */
- uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
- /* start sector of key material */
- uint32_t key_offset;
- /* number of anti-forensic stripes */
- uint32_t stripes;
-} QEMU_PACKED;
-
QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48);
-
-
-/*
- * This struct is written to disk in big-endian format,
- * but operated upon in native-endian format.
- */
-struct QCryptoBlockLUKSHeader {
- /* 'L', 'U', 'K', 'S', '0xBA', '0xBE' */
- char magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN];
-
- /* LUKS version, currently 1 */
- uint16_t version;
-
- /* cipher name specification (aes, etc) */
- char cipher_name[QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN];
-
- /* cipher mode specification (cbc-plain, xts-essiv:sha256, etc) */
- char cipher_mode[QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN];
-
- /* hash specification (sha256, etc) */
- char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN];
-
- /* start offset of the volume data (in 512 byte sectors) */
- uint32_t payload_offset;
-
- /* Number of key bytes */
- uint32_t key_bytes;
-
- /* master key checksum after PBKDF2 */
- uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
-
- /* salt for master key PBKDF2 */
- uint8_t master_key_salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
-
- /* iterations for master key PBKDF2 */
- uint32_t master_key_iterations;
-
- /* UUID of the partition in standard ASCII representation */
- uint8_t uuid[QCRYPTO_BLOCK_LUKS_UUID_LEN];
-
- /* key slots */
- QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS];
-} QEMU_PACKED;
-
QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592);
struct QCryptoBlockLUKS {
QCryptoBlockLUKSHeader header;
- /* Cache parsed versions of what's in header fields,
- * as we can't rely on QCryptoBlock.cipher being
- * non-NULL */
+ /* Main encryption algorithm used for encryption*/
QCryptoCipherAlgorithm cipher_alg;
+
+ /* Mode of encryption for the selected encryption algorithm */
QCryptoCipherMode cipher_mode;
+
+ /* Initialization vector generation algorithm */
QCryptoIVGenAlgorithm ivgen_alg;
+
+ /* Hash algorithm used for IV generation*/
QCryptoHashAlgorithm ivgen_hash_alg;
+
+ /*
+ * Encryption algorithm used for IV generation.
+ * Usually the same as main encryption algorithm
+ */
+ QCryptoCipherAlgorithm ivgen_cipher_alg;
+
+ /* Hash algorithm used in pbkdf2 function */
QCryptoHashAlgorithm hash_alg;
+
+ /* Name of the secret that was used to open the image */
+ char *secret;
};
@@ -235,7 +172,7 @@ static int qcrypto_block_luks_cipher_name_lookup(const char *name,
}
}
- error_setg(errp, "Algorithm %s with key size %d bytes not supported",
+ error_setg(errp, "Algorithm '%s' with key size %d bytes not supported",
name, key_bytes);
return 0;
}
@@ -271,7 +208,7 @@ static int qcrypto_block_luks_name_lookup(const char *name,
int ret = qapi_enum_parse(map, name, -1, NULL);
if (ret < 0) {
- error_setg(errp, "%s %s not supported", type, name);
+ error_setg(errp, "%s '%s' not supported", type, name);
return 0;
}
return ret;
@@ -318,7 +255,7 @@ qcrypto_block_luks_has_format(const uint8_t *buf,
*
* When calculating ESSIV IVs, the cipher length used by ESSIV
* may be different from the cipher length used for the block
- * encryption, becauses dm-crypt uses the hash digest length
+ * encryption, because dm-crypt uses the hash digest length
* as the key size. ie, if you have AES 128 as the block cipher
* and SHA 256 as ESSIV hash, then ESSIV will use AES 256 as
* the cipher since that gets a key length matching the digest
@@ -398,6 +335,531 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
}
/*
+ * Returns number of sectors needed to store the key material
+ * given number of anti forensic stripes
+ */
+static int
+qcrypto_block_luks_splitkeylen_sectors(const QCryptoBlockLUKS *luks,
+ unsigned int header_sectors,
+ unsigned int stripes)
+{
+ /*
+ * This calculation doesn't match that shown in the spec,
+ * but instead follows the cryptsetup implementation.
+ */
+
+ size_t splitkeylen = luks->header.master_key_len * stripes;
+
+ /* First align the key material size to block size*/
+ size_t splitkeylen_sectors =
+ DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
+
+ /* Then also align the key material size to the size of the header */
+ return ROUND_UP(splitkeylen_sectors, header_sectors);
+}
+
+
+void
+qcrypto_block_luks_to_disk_endian(QCryptoBlockLUKSHeader *hdr)
+{
+ size_t i;
+
+ /*
+ * Everything on disk uses Big Endian (tm), so flip header fields
+ * before writing them
+ */
+ cpu_to_be16s(&hdr->version);
+ cpu_to_be32s(&hdr->payload_offset_sector);
+ cpu_to_be32s(&hdr->master_key_len);
+ cpu_to_be32s(&hdr->master_key_iterations);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ cpu_to_be32s(&hdr->key_slots[i].active);
+ cpu_to_be32s(&hdr->key_slots[i].iterations);
+ cpu_to_be32s(&hdr->key_slots[i].key_offset_sector);
+ cpu_to_be32s(&hdr->key_slots[i].stripes);
+ }
+}
+
+void
+qcrypto_block_luks_from_disk_endian(QCryptoBlockLUKSHeader *hdr)
+{
+ size_t i;
+
+ /*
+ * The header is always stored in big-endian format, so
+ * convert everything to native
+ */
+ be16_to_cpus(&hdr->version);
+ be32_to_cpus(&hdr->payload_offset_sector);
+ be32_to_cpus(&hdr->master_key_len);
+ be32_to_cpus(&hdr->master_key_iterations);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ be32_to_cpus(&hdr->key_slots[i].active);
+ be32_to_cpus(&hdr->key_slots[i].iterations);
+ be32_to_cpus(&hdr->key_slots[i].key_offset_sector);
+ be32_to_cpus(&hdr->key_slots[i].stripes);
+ }
+}
+
+/*
+ * Stores the main LUKS header, taking care of endianness
+ */
+static int
+qcrypto_block_luks_store_header(QCryptoBlock *block,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ const QCryptoBlockLUKS *luks = block->opaque;
+ Error *local_err = NULL;
+ g_autofree QCryptoBlockLUKSHeader *hdr_copy = NULL;
+
+ /* Create a copy of the header */
+ hdr_copy = g_new0(QCryptoBlockLUKSHeader, 1);
+ memcpy(hdr_copy, &luks->header, sizeof(QCryptoBlockLUKSHeader));
+
+ qcrypto_block_luks_to_disk_endian(hdr_copy);
+
+ /* Write out the partition header and key slot headers */
+ writefunc(block, 0, (const uint8_t *)hdr_copy, sizeof(*hdr_copy),
+ opaque, &local_err);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Loads the main LUKS header, and byteswaps it to native endianness
+ * And run basic sanity checks on it
+ */
+static int
+qcrypto_block_luks_load_header(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ void *opaque,
+ Error **errp)
+{
+ int rv;
+ QCryptoBlockLUKS *luks = block->opaque;
+
+ /*
+ * Read the entire LUKS header, minus the key material from
+ * the underlying device
+ */
+ rv = readfunc(block, 0,
+ (uint8_t *)&luks->header,
+ sizeof(luks->header),
+ opaque,
+ errp);
+ if (rv < 0) {
+ return rv;
+ }
+
+ qcrypto_block_luks_from_disk_endian(&luks->header);
+
+ return 0;
+}
+
+/*
+ * Does basic sanity checks on the LUKS header
+ */
+static int
+qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks,
+ unsigned int flags,
+ Error **errp)
+{
+ size_t i, j;
+
+ unsigned int header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+ bool detached = flags & QCRYPTO_BLOCK_OPEN_DETACHED;
+
+ if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
+ QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
+ error_setg(errp, "Volume is not in LUKS format");
+ return -1;
+ }
+
+ if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
+ error_setg(errp, "LUKS version %" PRIu32 " is not supported",
+ luks->header.version);
+ return -1;
+ }
+
+ if (!memchr(luks->header.cipher_name, '\0',
+ sizeof(luks->header.cipher_name))) {
+ error_setg(errp, "LUKS header cipher name is not NUL terminated");
+ return -1;
+ }
+
+ if (!memchr(luks->header.cipher_mode, '\0',
+ sizeof(luks->header.cipher_mode))) {
+ error_setg(errp, "LUKS header cipher mode is not NUL terminated");
+ return -1;
+ }
+
+ if (!memchr(luks->header.hash_spec, '\0',
+ sizeof(luks->header.hash_spec))) {
+ error_setg(errp, "LUKS header hash spec is not NUL terminated");
+ return -1;
+ }
+
+ if (!detached && luks->header.payload_offset_sector <
+ DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) {
+ error_setg(errp, "LUKS payload is overlapping with the header");
+ return -1;
+ }
+
+ if (luks->header.master_key_iterations == 0) {
+ error_setg(errp, "LUKS key iteration count is zero");
+ return -1;
+ }
+
+ /* Check all keyslots for corruption */
+ for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
+
+ const QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
+ unsigned int start1 = slot1->key_offset_sector;
+ unsigned int len1 =
+ qcrypto_block_luks_splitkeylen_sectors(luks,
+ header_sectors,
+ slot1->stripes);
+
+ if (slot1->stripes != QCRYPTO_BLOCK_LUKS_STRIPES) {
+ error_setg(errp, "Keyslot %zu is corrupted (stripes %d != %d)",
+ i, slot1->stripes, QCRYPTO_BLOCK_LUKS_STRIPES);
+ return -1;
+ }
+
+ if (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
+ slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
+ error_setg(errp,
+ "Keyslot %zu state (active/disable) is corrupted", i);
+ return -1;
+ }
+
+ if (slot1->active == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED &&
+ slot1->iterations == 0) {
+ error_setg(errp, "Keyslot %zu iteration count is zero", i);
+ return -1;
+ }
+
+ if (start1 < DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) {
+ error_setg(errp,
+ "Keyslot %zu is overlapping with the LUKS header",
+ i);
+ return -1;
+ }
+
+ if (!detached && start1 + len1 > luks->header.payload_offset_sector) {
+ error_setg(errp,
+ "Keyslot %zu is overlapping with the encrypted payload",
+ i);
+ return -1;
+ }
+
+ for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
+ const QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
+ unsigned int start2 = slot2->key_offset_sector;
+ unsigned int len2 =
+ qcrypto_block_luks_splitkeylen_sectors(luks,
+ header_sectors,
+ slot2->stripes);
+
+ if (start1 + len1 > start2 && start2 + len2 > start1) {
+ error_setg(errp,
+ "Keyslots %zu and %zu are overlapping in the header",
+ i, j);
+ return -1;
+ }
+ }
+
+ }
+ return 0;
+}
+
+/*
+ * Parses the crypto parameters that are stored in the LUKS header
+ */
+
+static int
+qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
+{
+ g_autofree char *cipher_mode = g_strdup(luks->header.cipher_mode);
+ char *ivgen_name, *ivhash_name;
+ Error *local_err = NULL;
+
+ /*
+ * The cipher_mode header contains a string that we have
+ * to further parse, of the format
+ *
+ * <cipher-mode>-<iv-generator>[:<iv-hash>]
+ *
+ * eg cbc-essiv:sha256, cbc-plain64
+ */
+ ivgen_name = strchr(cipher_mode, '-');
+ if (!ivgen_name) {
+ error_setg(errp, "Unexpected cipher mode string format '%s'",
+ luks->header.cipher_mode);
+ return -1;
+ }
+ *ivgen_name = '\0';
+ ivgen_name++;
+
+ ivhash_name = strchr(ivgen_name, ':');
+ if (!ivhash_name) {
+ luks->ivgen_hash_alg = 0;
+ } else {
+ *ivhash_name = '\0';
+ ivhash_name++;
+
+ luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ }
+
+ luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+
+ luks->cipher_alg =
+ qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+ luks->cipher_mode,
+ luks->header.master_key_len,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+
+ luks->hash_alg =
+ qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+
+ luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+
+ if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
+ if (!ivhash_name) {
+ error_setg(errp, "Missing IV generator hash specification");
+ return -1;
+ }
+ luks->ivgen_cipher_alg =
+ qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
+ luks->ivgen_hash_alg,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ } else {
+
+ /*
+ * Note we parsed the ivhash_name earlier in the cipher_mode
+ * spec string even with plain/plain64 ivgens, but we
+ * will ignore it, since it is irrelevant for these ivgens.
+ * This is for compat with dm-crypt which will silently
+ * ignore hash names with these ivgens rather than report
+ * an error about the invalid usage
+ */
+ luks->ivgen_cipher_alg = luks->cipher_alg;
+ }
+ return 0;
+}
+
+/*
+ * Given a key slot, user password, and the master key,
+ * will store the encrypted master key there, and update the
+ * in-memory header. User must then write the in-memory header
+ *
+ * Returns:
+ * 0 if the keyslot was written successfully
+ * with the provided password
+ * -1 if a fatal error occurred while storing the key
+ */
+static int
+qcrypto_block_luks_store_key(QCryptoBlock *block,
+ unsigned int slot_idx,
+ const char *password,
+ uint8_t *masterkey,
+ uint64_t iter_time,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ QCryptoBlockLUKSKeySlot *slot;
+ g_autofree uint8_t *splitkey = NULL;
+ size_t splitkeylen;
+ g_autofree uint8_t *slotkey = NULL;
+ g_autoptr(QCryptoCipher) cipher = NULL;
+ g_autoptr(QCryptoIVGen) ivgen = NULL;
+ Error *local_err = NULL;
+ uint64_t iters;
+ int ret = -1;
+
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
+ splitkeylen = luks->header.master_key_len * slot->stripes;
+
+ if (qcrypto_random_bytes(slot->salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * Determine how many iterations are required to
+ * hash the user password while consuming 1 second of compute
+ * time
+ */
+ iters = qcrypto_pbkdf2_count_iters(luks->hash_alg,
+ (uint8_t *)password, strlen(password),
+ slot->salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ luks->header.master_key_len,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto cleanup;
+ }
+
+ if (iters > (ULLONG_MAX / iter_time)) {
+ error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu too large to scale",
+ (unsigned long long)iters);
+ goto cleanup;
+ }
+
+ /* iter_time was in millis, but count_iters reported for secs */
+ iters = iters * iter_time / 1000;
+
+ if (iters > UINT32_MAX) {
+ error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu larger than %u",
+ (unsigned long long)iters, UINT32_MAX);
+ goto cleanup;
+ }
+
+ slot->iterations =
+ MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+
+
+ /*
+ * Generate a key that we'll use to encrypt the master
+ * key, from the user's password
+ */
+ slotkey = g_new0(uint8_t, luks->header.master_key_len);
+ if (qcrypto_pbkdf2(luks->hash_alg,
+ (uint8_t *)password, strlen(password),
+ slot->salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ slot->iterations,
+ slotkey, luks->header.master_key_len,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+
+ /*
+ * Setup the encryption objects needed to encrypt the
+ * master key material
+ */
+ cipher = qcrypto_cipher_new(luks->cipher_alg,
+ luks->cipher_mode,
+ slotkey, luks->header.master_key_len,
+ errp);
+ if (!cipher) {
+ goto cleanup;
+ }
+
+ ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+ luks->ivgen_cipher_alg,
+ luks->ivgen_hash_alg,
+ slotkey, luks->header.master_key_len,
+ errp);
+ if (!ivgen) {
+ goto cleanup;
+ }
+
+ /*
+ * Before storing the master key, we need to vastly
+ * increase its size, as protection against forensic
+ * disk data recovery
+ */
+ splitkey = g_new0(uint8_t, splitkeylen);
+
+ if (qcrypto_afsplit_encode(luks->hash_alg,
+ luks->header.master_key_len,
+ slot->stripes,
+ masterkey,
+ splitkey,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ /*
+ * Now we encrypt the split master key with the key generated
+ * from the user's password, before storing it
+ */
+ if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ 0,
+ splitkey,
+ splitkeylen,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ /* Write out the slot's master key material. */
+ if (writefunc(block,
+ slot->key_offset_sector *
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ splitkey, splitkeylen,
+ opaque,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
+
+ if (qcrypto_block_luks_store_header(block, writefunc, opaque, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (slotkey) {
+ memset(slotkey, 0, luks->header.master_key_len);
+ }
+ if (splitkey) {
+ memset(splitkey, 0, splitkeylen);
+ }
+ return ret;
+}
+
+/*
* Given a key slot, and user password, this will attempt to unlock
* the master encryption key from the key slot.
*
@@ -410,38 +872,33 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher,
*/
static int
qcrypto_block_luks_load_key(QCryptoBlock *block,
- QCryptoBlockLUKSKeySlot *slot,
+ size_t slot_idx,
const char *password,
- QCryptoCipherAlgorithm cipheralg,
- QCryptoCipherMode ciphermode,
- QCryptoHashAlgorithm hash,
- QCryptoIVGenAlgorithm ivalg,
- QCryptoCipherAlgorithm ivcipheralg,
- QCryptoHashAlgorithm ivhash,
uint8_t *masterkey,
- size_t masterkeylen,
QCryptoBlockReadFunc readfunc,
void *opaque,
Error **errp)
{
QCryptoBlockLUKS *luks = block->opaque;
- uint8_t *splitkey;
+ const QCryptoBlockLUKSKeySlot *slot;
+ g_autofree uint8_t *splitkey = NULL;
size_t splitkeylen;
- uint8_t *possiblekey;
- int ret = -1;
- ssize_t rv;
- QCryptoCipher *cipher = NULL;
+ g_autofree uint8_t *possiblekey = NULL;
+ int rv;
+ g_autoptr(QCryptoCipher) cipher = NULL;
uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
- QCryptoIVGen *ivgen = NULL;
+ g_autoptr(QCryptoIVGen) ivgen = NULL;
size_t niv;
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
return 0;
}
- splitkeylen = masterkeylen * slot->stripes;
+ splitkeylen = luks->header.master_key_len * slot->stripes;
splitkey = g_new0(uint8_t, splitkeylen);
- possiblekey = g_new0(uint8_t, masterkeylen);
+ possiblekey = g_new0(uint8_t, luks->header.master_key_len);
/*
* The user password is used to generate a (possible)
@@ -450,13 +907,13 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
* the key is correct and validate the results of
* decryption later.
*/
- if (qcrypto_pbkdf2(hash,
+ if (qcrypto_pbkdf2(luks->hash_alg,
(const uint8_t *)password, strlen(password),
slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
- possiblekey, masterkeylen,
+ possiblekey, luks->header.master_key_len,
errp) < 0) {
- goto cleanup;
+ return -1;
}
/*
@@ -467,33 +924,37 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
* then encrypted.
*/
rv = readfunc(block,
- slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
splitkey, splitkeylen,
opaque,
errp);
if (rv < 0) {
- goto cleanup;
+ return -1;
}
/* Setup the cipher/ivgen that we'll use to try to decrypt
* the split master key material */
- cipher = qcrypto_cipher_new(cipheralg, ciphermode,
- possiblekey, masterkeylen,
+ cipher = qcrypto_cipher_new(luks->cipher_alg,
+ luks->cipher_mode,
+ possiblekey,
+ luks->header.master_key_len,
errp);
if (!cipher) {
- goto cleanup;
+ return -1;
}
- niv = qcrypto_cipher_get_iv_len(cipheralg,
- ciphermode);
- ivgen = qcrypto_ivgen_new(ivalg,
- ivcipheralg,
- ivhash,
- possiblekey, masterkeylen,
+ niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+ luks->cipher_mode);
+
+ ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+ luks->ivgen_cipher_alg,
+ luks->ivgen_hash_alg,
+ possiblekey,
+ luks->header.master_key_len,
errp);
if (!ivgen) {
- goto cleanup;
+ return -1;
}
@@ -504,28 +965,28 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
* to reset the encryption cipher every time the master
* key crosses a sector boundary.
*/
- if (qcrypto_block_decrypt_helper(cipher,
- niv,
- ivgen,
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
- 0,
- splitkey,
- splitkeylen,
- errp) < 0) {
- goto cleanup;
+ if (qcrypto_block_cipher_decrypt_helper(cipher,
+ niv,
+ ivgen,
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ 0,
+ splitkey,
+ splitkeylen,
+ errp) < 0) {
+ return -1;
}
/*
* Now we've decrypted the split master key, join
* it back together to get the actual master key.
*/
- if (qcrypto_afsplit_decode(hash,
- masterkeylen,
+ if (qcrypto_afsplit_decode(luks->hash_alg,
+ luks->header.master_key_len,
slot->stripes,
splitkey,
masterkey,
errp) < 0) {
- goto cleanup;
+ return -1;
}
@@ -537,33 +998,27 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
* then comparing that to the hash stored in the key slot
* header
*/
- if (qcrypto_pbkdf2(hash,
- masterkey, masterkeylen,
+ if (qcrypto_pbkdf2(luks->hash_alg,
+ masterkey,
+ luks->header.master_key_len,
luks->header.master_key_salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_iterations,
- keydigest, G_N_ELEMENTS(keydigest),
+ keydigest,
+ G_N_ELEMENTS(keydigest),
errp) < 0) {
- goto cleanup;
+ return -1;
}
if (memcmp(keydigest, luks->header.master_key_digest,
QCRYPTO_BLOCK_LUKS_DIGEST_LEN) == 0) {
/* Success, we got the right master key */
- ret = 1;
- goto cleanup;
+ return 1;
}
/* Fail, user's password was not valid for this key slot,
* tell caller to try another slot */
- ret = 0;
-
- cleanup:
- qcrypto_ivgen_free(ivgen);
- qcrypto_cipher_free(cipher);
- g_free(splitkey);
- g_free(possiblekey);
- return ret;
+ return 0;
}
@@ -577,37 +1032,19 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
static int
qcrypto_block_luks_find_key(QCryptoBlock *block,
const char *password,
- QCryptoCipherAlgorithm cipheralg,
- QCryptoCipherMode ciphermode,
- QCryptoHashAlgorithm hash,
- QCryptoIVGenAlgorithm ivalg,
- QCryptoCipherAlgorithm ivcipheralg,
- QCryptoHashAlgorithm ivhash,
- uint8_t **masterkey,
- size_t *masterkeylen,
+ uint8_t *masterkey,
QCryptoBlockReadFunc readfunc,
void *opaque,
Error **errp)
{
- QCryptoBlockLUKS *luks = block->opaque;
size_t i;
int rv;
- *masterkey = g_new0(uint8_t, luks->header.key_bytes);
- *masterkeylen = luks->header.key_bytes;
-
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
rv = qcrypto_block_luks_load_key(block,
- &luks->header.key_slots[i],
+ i,
password,
- cipheralg,
- ciphermode,
- hash,
- ivalg,
- ivcipheralg,
- ivhash,
- *masterkey,
- *masterkeylen,
+ masterkey,
readfunc,
opaque,
errp);
@@ -620,14 +1057,130 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
}
error_setg(errp, "Invalid password, cannot unlock any keyslot");
-
error:
- g_free(*masterkey);
- *masterkey = NULL;
- *masterkeylen = 0;
return -1;
}
+/*
+ * Returns true if a slot i is marked as active
+ * (contains encrypted copy of the master key)
+ */
+static bool
+qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks,
+ unsigned int slot_idx)
+{
+ uint32_t val;
+
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ val = luks->header.key_slots[slot_idx].active;
+ return val == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
+}
+
+/*
+ * Returns the number of slots that are marked as active
+ * (slots that contain encrypted copy of the master key)
+ */
+static unsigned int
+qcrypto_block_luks_count_active_slots(const QCryptoBlockLUKS *luks)
+{
+ size_t i = 0;
+ unsigned int ret = 0;
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (qcrypto_block_luks_slot_active(luks, i)) {
+ ret++;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Finds first key slot which is not active
+ * Returns the key slot index, or -1 if it doesn't exist
+ */
+static int
+qcrypto_block_luks_find_free_keyslot(const QCryptoBlockLUKS *luks)
+{
+ size_t i;
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (!qcrypto_block_luks_slot_active(luks, i)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Erases an keyslot given its index
+ * Returns:
+ * 0 if the keyslot was erased successfully
+ * -1 if a error occurred while erasing the keyslot
+ *
+ */
+static int
+qcrypto_block_luks_erase_key(QCryptoBlock *block,
+ unsigned int slot_idx,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ QCryptoBlockLUKSKeySlot *slot;
+ g_autofree uint8_t *garbagesplitkey = NULL;
+ size_t splitkeylen;
+ size_t i;
+ Error *local_err = NULL;
+ int ret;
+
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
+
+ splitkeylen = luks->header.master_key_len * slot->stripes;
+ assert(splitkeylen > 0);
+
+ garbagesplitkey = g_new0(uint8_t, splitkeylen);
+
+ /* Reset the key slot header */
+ memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
+ slot->iterations = 0;
+ slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+
+ ret = qcrypto_block_luks_store_header(block, writefunc,
+ opaque, &local_err);
+
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ }
+ /*
+ * Now try to erase the key material, even if the header
+ * update failed
+ */
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS; i++) {
+ if (qcrypto_random_bytes(garbagesplitkey,
+ splitkeylen, &local_err) < 0) {
+ /*
+ * If we failed to get the random data, still write
+ * at least zeros to the key slot at least once
+ */
+ error_propagate(errp, local_err);
+
+ if (i > 0) {
+ return -1;
+ }
+ }
+ if (writefunc(block,
+ slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ garbagesplitkey,
+ splitkeylen,
+ opaque,
+ &local_err) < 0) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ }
+ return ret;
+}
static int
qcrypto_block_luks_open(QCryptoBlock *block,
@@ -636,23 +1189,12 @@ qcrypto_block_luks_open(QCryptoBlock *block,
QCryptoBlockReadFunc readfunc,
void *opaque,
unsigned int flags,
+ size_t n_threads,
Error **errp)
{
- QCryptoBlockLUKS *luks;
- Error *local_err = NULL;
- int ret = 0;
- size_t i;
- ssize_t rv;
- uint8_t *masterkey = NULL;
- size_t masterkeylen;
- char *ivgen_name, *ivhash_name;
- QCryptoCipherMode ciphermode;
- QCryptoCipherAlgorithm cipheralg;
- QCryptoIVGenAlgorithm ivalg;
- QCryptoCipherAlgorithm ivcipheralg;
- QCryptoHashAlgorithm hash;
- QCryptoHashAlgorithm ivhash;
- char *password = NULL;
+ QCryptoBlockLUKS *luks = NULL;
+ g_autofree uint8_t *masterkey = NULL;
+ g_autofree char *password = NULL;
if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
if (!options->u.luks.key_secret) {
@@ -669,205 +1211,76 @@ qcrypto_block_luks_open(QCryptoBlock *block,
luks = g_new0(QCryptoBlockLUKS, 1);
block->opaque = luks;
+ luks->secret = g_strdup(options->u.luks.key_secret);
- /* Read the entire LUKS header, minus the key material from
- * the underlying device */
- rv = readfunc(block, 0,
- (uint8_t *)&luks->header,
- sizeof(luks->header),
- opaque,
- errp);
- if (rv < 0) {
- ret = rv;
+ if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) {
goto fail;
}
- /* The header is always stored in big-endian format, so
- * convert everything to native */
- be16_to_cpus(&luks->header.version);
- be32_to_cpus(&luks->header.payload_offset);
- be32_to_cpus(&luks->header.key_bytes);
- be32_to_cpus(&luks->header.master_key_iterations);
-
- for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- be32_to_cpus(&luks->header.key_slots[i].active);
- be32_to_cpus(&luks->header.key_slots[i].iterations);
- be32_to_cpus(&luks->header.key_slots[i].key_offset);
- be32_to_cpus(&luks->header.key_slots[i].stripes);
- }
-
- if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
- QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
- error_setg(errp, "Volume is not in LUKS format");
- ret = -EINVAL;
- goto fail;
- }
- if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
- error_setg(errp, "LUKS version %" PRIu32 " is not supported",
- luks->header.version);
- ret = -ENOTSUP;
+ if (qcrypto_block_luks_check_header(luks, flags, errp) < 0) {
goto fail;
}
- /*
- * The cipher_mode header contains a string that we have
- * to further parse, of the format
- *
- * <cipher-mode>-<iv-generator>[:<iv-hash>]
- *
- * eg cbc-essiv:sha256, cbc-plain64
- */
- ivgen_name = strchr(luks->header.cipher_mode, '-');
- if (!ivgen_name) {
- ret = -EINVAL;
- error_setg(errp, "Unexpected cipher mode string format %s",
- luks->header.cipher_mode);
+ if (qcrypto_block_luks_parse_header(luks, errp) < 0) {
goto fail;
}
- *ivgen_name = '\0';
- ivgen_name++;
-
- ivhash_name = strchr(ivgen_name, ':');
- if (!ivhash_name) {
- ivhash = 0;
- } else {
- *ivhash_name = '\0';
- ivhash_name++;
-
- ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
- }
-
- ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
-
- cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
- ciphermode,
- luks->header.key_bytes,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
-
- hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
-
- ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
-
- if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) {
- if (!ivhash_name) {
- ret = -EINVAL;
- error_setg(errp, "Missing IV generator hash specification");
- goto fail;
- }
- ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg,
- ivhash,
- &local_err);
- if (local_err) {
- ret = -ENOTSUP;
- error_propagate(errp, local_err);
- goto fail;
- }
- } else {
- /* Note we parsed the ivhash_name earlier in the cipher_mode
- * spec string even with plain/plain64 ivgens, but we
- * will ignore it, since it is irrelevant for these ivgens.
- * This is for compat with dm-crypt which will silently
- * ignore hash names with these ivgens rather than report
- * an error about the invalid usage
- */
- ivcipheralg = cipheralg;
- }
if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
/* Try to find which key slot our password is valid for
* and unlock the master key from that slot.
*/
+
+ masterkey = g_new0(uint8_t, luks->header.master_key_len);
+
if (qcrypto_block_luks_find_key(block,
password,
- cipheralg, ciphermode,
- hash,
- ivalg,
- ivcipheralg,
- ivhash,
- &masterkey, &masterkeylen,
+ masterkey,
readfunc, opaque,
errp) < 0) {
- ret = -EACCES;
goto fail;
}
/* We have a valid master key now, so can setup the
* block device payload decryption objects
*/
- block->kdfhash = hash;
- block->niv = qcrypto_cipher_get_iv_len(cipheralg,
- ciphermode);
- block->ivgen = qcrypto_ivgen_new(ivalg,
- ivcipheralg,
- ivhash,
- masterkey, masterkeylen,
+ block->kdfhash = luks->hash_alg;
+ block->niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+ luks->cipher_mode);
+
+ block->ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+ luks->ivgen_cipher_alg,
+ luks->ivgen_hash_alg,
+ masterkey,
+ luks->header.master_key_len,
errp);
if (!block->ivgen) {
- ret = -ENOTSUP;
goto fail;
}
- block->cipher = qcrypto_cipher_new(cipheralg,
- ciphermode,
- masterkey, masterkeylen,
- errp);
- if (!block->cipher) {
- ret = -ENOTSUP;
+ if (qcrypto_block_init_cipher(block,
+ luks->cipher_alg,
+ luks->cipher_mode,
+ masterkey,
+ luks->header.master_key_len,
+ n_threads,
+ errp) < 0) {
goto fail;
}
}
block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
- block->payload_offset = luks->header.payload_offset *
+ block->payload_offset = luks->header.payload_offset_sector *
block->sector_size;
-
- luks->cipher_alg = cipheralg;
- luks->cipher_mode = ciphermode;
- luks->ivgen_alg = ivalg;
- luks->ivgen_hash_alg = ivhash;
- luks->hash_alg = hash;
-
- g_free(masterkey);
- g_free(password);
+ block->detached_header = (block->payload_offset == 0) ? true : false;
return 0;
fail:
- g_free(masterkey);
- qcrypto_cipher_free(block->cipher);
+ qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);
+ g_free(luks->secret);
g_free(luks);
- g_free(password);
- return ret;
+ return -1;
}
@@ -891,26 +1304,23 @@ qcrypto_block_luks_create(QCryptoBlock *block,
QCryptoBlockLUKS *luks;
QCryptoBlockCreateOptionsLUKS luks_opts;
Error *local_err = NULL;
- uint8_t *masterkey = NULL;
- uint8_t *slotkey = NULL;
- uint8_t *splitkey = NULL;
- size_t splitkeylen = 0;
+ g_autofree uint8_t *masterkey = NULL;
+ size_t header_sectors;
+ size_t split_key_sectors;
size_t i;
- QCryptoCipher *cipher = NULL;
- QCryptoIVGen *ivgen = NULL;
- char *password;
+ g_autofree char *password = NULL;
const char *cipher_alg;
const char *cipher_mode;
const char *ivgen_alg;
const char *ivgen_hash_alg = NULL;
const char *hash_alg;
- char *cipher_mode_spec = NULL;
- QCryptoCipherAlgorithm ivcipheralg = 0;
+ g_autofree char *cipher_mode_spec = NULL;
uint64_t iters;
+ uint64_t detached_header_size;
memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
if (!luks_opts.has_iter_time) {
- luks_opts.iter_time = 2000;
+ luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
}
if (!luks_opts.has_cipher_alg) {
luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
@@ -930,6 +1340,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
luks_opts.has_ivgen_hash_alg = true;
}
}
+
+ luks = g_new0(QCryptoBlockLUKS, 1);
+ block->opaque = luks;
+
+ luks->cipher_alg = luks_opts.cipher_alg;
+ luks->cipher_mode = luks_opts.cipher_mode;
+ luks->ivgen_alg = luks_opts.ivgen_alg;
+ luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
+ luks->hash_alg = luks_opts.hash_alg;
+
+
/* Note we're allowing ivgen_hash_alg to be set even for
* non-essiv iv generators that don't need a hash. It will
* be silently ignored, for compatibility with dm-crypt */
@@ -937,15 +1358,15 @@ qcrypto_block_luks_create(QCryptoBlock *block,
if (!options->u.luks.key_secret) {
error_setg(errp, "Parameter '%skey-secret' is required for cipher",
optprefix ? optprefix : "");
- return -1;
+ goto error;
}
+ luks->secret = g_strdup(options->u.luks.key_secret);
+
password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
if (!password) {
- return -1;
+ goto error;
}
- luks = g_new0(QCryptoBlockLUKS, 1);
- block->opaque = luks;
memcpy(luks->header.magic, qcrypto_block_luks_magic,
QCRYPTO_BLOCK_LUKS_MAGIC_LEN);
@@ -992,24 +1413,27 @@ qcrypto_block_luks_create(QCryptoBlock *block,
}
if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
- ivcipheralg = qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg,
- luks_opts.ivgen_hash_alg,
- &local_err);
+ luks->ivgen_cipher_alg =
+ qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg,
+ luks_opts.ivgen_hash_alg,
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
}
} else {
- ivcipheralg = luks_opts.cipher_alg;
+ luks->ivgen_cipher_alg = luks_opts.cipher_alg;
}
strcpy(luks->header.cipher_name, cipher_alg);
strcpy(luks->header.cipher_mode, cipher_mode_spec);
strcpy(luks->header.hash_spec, hash_alg);
- luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+ luks->header.master_key_len =
+ qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+
if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) {
- luks->header.key_bytes *= 2;
+ luks->header.master_key_len *= 2;
}
/* Generate the salt used for hashing the master key
@@ -1022,19 +1446,17 @@ qcrypto_block_luks_create(QCryptoBlock *block,
}
/* Generate random master key */
- masterkey = g_new0(uint8_t, luks->header.key_bytes);
+ masterkey = g_new0(uint8_t, luks->header.master_key_len);
if (qcrypto_random_bytes(masterkey,
- luks->header.key_bytes, errp) < 0) {
+ luks->header.master_key_len, errp) < 0) {
goto error;
}
/* Setup the block device payload encryption objects */
- block->cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
- luks_opts.cipher_mode,
- masterkey, luks->header.key_bytes,
- errp);
- if (!block->cipher) {
+ if (qcrypto_block_init_cipher(block, luks_opts.cipher_alg,
+ luks_opts.cipher_mode, masterkey,
+ luks->header.master_key_len, 1, errp) < 0) {
goto error;
}
@@ -1042,9 +1464,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
block->niv = qcrypto_cipher_get_iv_len(luks_opts.cipher_alg,
luks_opts.cipher_mode);
block->ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
- ivcipheralg,
+ luks->ivgen_cipher_alg,
luks_opts.ivgen_hash_alg,
- masterkey, luks->header.key_bytes,
+ masterkey, luks->header.master_key_len,
errp);
if (!block->ivgen) {
@@ -1056,7 +1478,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
* key, in order to have 1 second of compute time used
*/
iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
- masterkey, luks->header.key_bytes,
+ masterkey, luks->header.master_key_len,
luks->header.master_key_salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
QCRYPTO_BLOCK_LUKS_DIGEST_LEN,
@@ -1096,7 +1518,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
* valid master key
*/
if (qcrypto_pbkdf2(luks_opts.hash_alg,
- masterkey, luks->header.key_bytes,
+ masterkey, luks->header.master_key_len,
luks->header.master_key_salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_iterations,
@@ -1106,253 +1528,360 @@ qcrypto_block_luks_create(QCryptoBlock *block,
goto error;
}
+ /* start with the sector that follows the header*/
+ header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+ QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+ split_key_sectors =
+ qcrypto_block_luks_splitkeylen_sectors(luks,
+ header_sectors,
+ QCRYPTO_BLOCK_LUKS_STRIPES);
- /* Although LUKS has multiple key slots, we're just going
- * to use the first key slot */
- splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES;
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- luks->header.key_slots[i].active = i == 0 ?
- QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED :
- QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
- luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
+ QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
+ slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
- /* This calculation doesn't match that shown in the spec,
- * but instead follows the cryptsetup implementation.
- */
- luks->header.key_slots[i].key_offset =
- (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
- (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
- (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
+ slot->key_offset_sector = header_sectors + i * split_key_sectors;
+ slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
}
- if (qcrypto_random_bytes(luks->header.key_slots[0].salt,
- QCRYPTO_BLOCK_LUKS_SALT_LEN,
- errp) < 0) {
- goto error;
+ if (block->detached_header) {
+ /*
+ * For a detached LUKS header image, set the payload_offset_sector
+ * to 0 to specify the starting point for read/write
+ */
+ luks->header.payload_offset_sector = 0;
+ } else {
+ /*
+ * The total size of the LUKS headers is the partition header + key
+ * slot headers, rounded up to the nearest sector, combined with
+ * the size of each master key material region, also rounded up
+ * to the nearest sector
+ */
+ luks->header.payload_offset_sector = header_sectors +
+ QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors;
}
- /* Again we determine how many iterations are required to
- * hash the user password while consuming 1 second of compute
- * time */
- iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
- (uint8_t *)password, strlen(password),
- luks->header.key_slots[0].salt,
- QCRYPTO_BLOCK_LUKS_SALT_LEN,
- luks->header.key_bytes,
- &local_err);
+ block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+ block->payload_offset = luks->header.payload_offset_sector *
+ block->sector_size;
+ detached_header_size =
+ (header_sectors + QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS *
+ split_key_sectors) * block->sector_size;
+
+ /* Reserve header space to match payload offset */
+ initfunc(block, detached_header_size, opaque, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
}
- if (iters > (ULLONG_MAX / luks_opts.iter_time)) {
- error_setg_errno(errp, ERANGE,
- "PBKDF iterations %llu too large to scale",
- (unsigned long long)iters);
+
+ /* populate the slot 0 with the password encrypted master key*/
+ /* This will also store the header */
+ if (qcrypto_block_luks_store_key(block,
+ 0,
+ password,
+ masterkey,
+ luks_opts.iter_time,
+ writefunc,
+ opaque,
+ errp) < 0) {
goto error;
}
- /* iter_time was in millis, but count_iters reported for secs */
- iters = iters * luks_opts.iter_time / 1000;
+ memset(masterkey, 0, luks->header.master_key_len);
- if (iters > UINT32_MAX) {
- error_setg_errno(errp, ERANGE,
- "PBKDF iterations %llu larger than %u",
- (unsigned long long)iters, UINT32_MAX);
- goto error;
+ return 0;
+
+ error:
+ if (masterkey) {
+ memset(masterkey, 0, luks->header.master_key_len);
}
- luks->header.key_slots[0].iterations =
- MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+ qcrypto_block_free_cipher(block);
+ qcrypto_ivgen_free(block->ivgen);
+ g_free(luks->secret);
+ g_free(luks);
+ return -1;
+}
- /* Generate a key that we'll use to encrypt the master
- * key, from the user's password
- */
- slotkey = g_new0(uint8_t, luks->header.key_bytes);
- if (qcrypto_pbkdf2(luks_opts.hash_alg,
- (uint8_t *)password, strlen(password),
- luks->header.key_slots[0].salt,
- QCRYPTO_BLOCK_LUKS_SALT_LEN,
- luks->header.key_slots[0].iterations,
- slotkey, luks->header.key_bytes,
- errp) < 0) {
- goto error;
+static int
+qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ uint64_t iter_time = opts_luks->has_iter_time ?
+ opts_luks->iter_time :
+ QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
+ int keyslot;
+ g_autofree char *old_password = NULL;
+ g_autofree char *new_password = NULL;
+ g_autofree uint8_t *master_key = NULL;
+
+ char *secret = opts_luks->secret ?: luks->secret;
+
+ if (!opts_luks->new_secret) {
+ error_setg(errp, "'new-secret' is required to activate a keyslot");
+ return -1;
+ }
+ if (opts_luks->old_secret) {
+ error_setg(errp,
+ "'old-secret' must not be given when activating keyslots");
+ return -1;
}
+ if (opts_luks->has_keyslot) {
+ keyslot = opts_luks->keyslot;
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
+ error_setg(errp,
+ "Invalid keyslot %u specified, must be between 0 and %u",
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
+ return -1;
+ }
+ } else {
+ keyslot = qcrypto_block_luks_find_free_keyslot(luks);
+ if (keyslot == -1) {
+ error_setg(errp,
+ "Can't add a keyslot - all keyslots are in use");
+ return -1;
+ }
+ }
- /* Setup the encryption objects needed to encrypt the
- * master key material
- */
- cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
- luks_opts.cipher_mode,
- slotkey, luks->header.key_bytes,
- errp);
- if (!cipher) {
- goto error;
+ if (!force && qcrypto_block_luks_slot_active(luks, keyslot)) {
+ error_setg(errp,
+ "Refusing to overwrite active keyslot %i - "
+ "please erase it first",
+ keyslot);
+ return -1;
}
- ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg,
- ivcipheralg,
- luks_opts.ivgen_hash_alg,
- slotkey, luks->header.key_bytes,
- errp);
- if (!ivgen) {
- goto error;
+ /* Locate the password that will be used to retrieve the master key */
+ old_password = qcrypto_secret_lookup_as_utf8(secret, errp);
+ if (!old_password) {
+ return -1;
}
- /* Before storing the master key, we need to vastly
- * increase its size, as protection against forensic
- * disk data recovery */
- splitkey = g_new0(uint8_t, splitkeylen);
+ /* Retrieve the master key */
+ master_key = g_new0(uint8_t, luks->header.master_key_len);
- if (qcrypto_afsplit_encode(luks_opts.hash_alg,
- luks->header.key_bytes,
- luks->header.key_slots[0].stripes,
- masterkey,
- splitkey,
- errp) < 0) {
- goto error;
+ if (qcrypto_block_luks_find_key(block, old_password, master_key,
+ readfunc, opaque, errp) < 0) {
+ error_append_hint(errp, "Failed to retrieve the master key");
+ return -1;
}
- /* Now we encrypt the split master key with the key generated
- * from the user's password, before storing it */
- if (qcrypto_block_encrypt_helper(cipher, block->niv, ivgen,
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
- 0,
- splitkey,
- splitkeylen,
- errp) < 0) {
- goto error;
+ /* Locate the new password*/
+ new_password = qcrypto_secret_lookup_as_utf8(opts_luks->new_secret, errp);
+ if (!new_password) {
+ return -1;
}
+ /* Now set the new keyslots */
+ if (qcrypto_block_luks_store_key(block, keyslot, new_password, master_key,
+ iter_time, writefunc, opaque, errp)) {
+ error_append_hint(errp, "Failed to write to keyslot %i", keyslot);
+ return -1;
+ }
+ return 0;
+}
- /* The total size of the LUKS headers is the partition header + key
- * slot headers, rounded up to the nearest sector, combined with
- * the size of each master key material region, also rounded up
- * to the nearest sector */
- luks->header.payload_offset =
- (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
- (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
- (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
- QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
-
- block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
- block->payload_offset = luks->header.payload_offset *
- block->sector_size;
+static int
+qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ g_autofree uint8_t *tmpkey = NULL;
+ g_autofree char *old_password = NULL;
- /* Reserve header space to match payload offset */
- initfunc(block, block->payload_offset, opaque, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto error;
+ if (opts_luks->new_secret) {
+ error_setg(errp,
+ "'new-secret' must not be given when erasing keyslots");
+ return -1;
+ }
+ if (opts_luks->has_iter_time) {
+ error_setg(errp,
+ "'iter-time' must not be given when erasing keyslots");
+ return -1;
+ }
+ if (opts_luks->secret) {
+ error_setg(errp,
+ "'secret' must not be given when erasing keyslots");
+ return -1;
}
- /* Everything on disk uses Big Endian, so flip header fields
- * before writing them */
- cpu_to_be16s(&luks->header.version);
- cpu_to_be32s(&luks->header.payload_offset);
- cpu_to_be32s(&luks->header.key_bytes);
- cpu_to_be32s(&luks->header.master_key_iterations);
+ /* Load the old password if given */
+ if (opts_luks->old_secret) {
+ old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret,
+ errp);
+ if (!old_password) {
+ return -1;
+ }
- for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- cpu_to_be32s(&luks->header.key_slots[i].active);
- cpu_to_be32s(&luks->header.key_slots[i].iterations);
- cpu_to_be32s(&luks->header.key_slots[i].key_offset);
- cpu_to_be32s(&luks->header.key_slots[i].stripes);
+ /*
+ * Allocate a temporary key buffer that we will need when
+ * checking if slot matches the given old password
+ */
+ tmpkey = g_new0(uint8_t, luks->header.master_key_len);
}
+ /* Erase an explicitly given keyslot */
+ if (opts_luks->has_keyslot) {
+ int keyslot = opts_luks->keyslot;
- /* Write out the partition header and key slot headers */
- writefunc(block, 0,
- (const uint8_t *)&luks->header,
- sizeof(luks->header),
- opaque,
- &local_err);
-
- /* Delay checking local_err until we've byte-swapped */
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
+ error_setg(errp,
+ "Invalid keyslot %i specified, must be between 0 and %i",
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
+ return -1;
+ }
- /* Byte swap the header back to native, in case we need
- * to read it again later */
- be16_to_cpus(&luks->header.version);
- be32_to_cpus(&luks->header.payload_offset);
- be32_to_cpus(&luks->header.key_bytes);
- be32_to_cpus(&luks->header.master_key_iterations);
+ if (opts_luks->old_secret) {
+ int rv = qcrypto_block_luks_load_key(block,
+ keyslot,
+ old_password,
+ tmpkey,
+ readfunc,
+ opaque,
+ errp);
+ if (rv == -1) {
+ return -1;
+ } else if (rv == 0) {
+ error_setg(errp,
+ "Given keyslot %i doesn't contain the given "
+ "old password for erase operation",
+ keyslot);
+ return -1;
+ }
+ }
- for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- be32_to_cpus(&luks->header.key_slots[i].active);
- be32_to_cpus(&luks->header.key_slots[i].iterations);
- be32_to_cpus(&luks->header.key_slots[i].key_offset);
- be32_to_cpus(&luks->header.key_slots[i].stripes);
- }
+ if (!force && !qcrypto_block_luks_slot_active(luks, keyslot)) {
+ error_setg(errp,
+ "Given keyslot %i is already erased (inactive) ",
+ keyslot);
+ return -1;
+ }
- if (local_err) {
- error_propagate(errp, local_err);
- goto error;
- }
+ if (!force && qcrypto_block_luks_count_active_slots(luks) == 1) {
+ error_setg(errp,
+ "Attempt to erase the only active keyslot %i "
+ "which will erase all the data in the image "
+ "irreversibly - refusing operation",
+ keyslot);
+ return -1;
+ }
- /* Write out the master key material, starting at the
- * sector immediately following the partition header. */
- if (writefunc(block,
- luks->header.key_slots[0].key_offset *
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
- splitkey, splitkeylen,
- opaque,
- errp) != splitkeylen) {
- goto error;
- }
+ if (qcrypto_block_luks_erase_key(block, keyslot,
+ writefunc, opaque, errp)) {
+ error_append_hint(errp, "Failed to erase keyslot %i", keyslot);
+ return -1;
+ }
- luks->cipher_alg = luks_opts.cipher_alg;
- luks->cipher_mode = luks_opts.cipher_mode;
- luks->ivgen_alg = luks_opts.ivgen_alg;
- luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
- luks->hash_alg = luks_opts.hash_alg;
+ /* Erase all keyslots that match the given old password */
+ } else if (opts_luks->old_secret) {
+
+ unsigned long slots_to_erase_bitmap = 0;
+ size_t i;
+ int slot_count;
+
+ assert(QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS <=
+ sizeof(slots_to_erase_bitmap) * 8);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ int rv = qcrypto_block_luks_load_key(block,
+ i,
+ old_password,
+ tmpkey,
+ readfunc,
+ opaque,
+ errp);
+ if (rv == -1) {
+ return -1;
+ } else if (rv == 1) {
+ bitmap_set(&slots_to_erase_bitmap, i, 1);
+ }
+ }
- memset(masterkey, 0, luks->header.key_bytes);
- g_free(masterkey);
- memset(slotkey, 0, luks->header.key_bytes);
- g_free(slotkey);
- g_free(splitkey);
- g_free(password);
- g_free(cipher_mode_spec);
+ slot_count = bitmap_count_one(&slots_to_erase_bitmap,
+ QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ if (slot_count == 0) {
+ error_setg(errp,
+ "No keyslots match given (old) password for erase operation");
+ return -1;
+ }
- qcrypto_ivgen_free(ivgen);
- qcrypto_cipher_free(cipher);
+ if (!force &&
+ slot_count == qcrypto_block_luks_count_active_slots(luks)) {
+ error_setg(errp,
+ "All the active keyslots match the (old) password that "
+ "was given and erasing them will erase all the data in "
+ "the image irreversibly - refusing operation");
+ return -1;
+ }
+ /* Now apply the update */
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (!test_bit(i, &slots_to_erase_bitmap)) {
+ continue;
+ }
+ if (qcrypto_block_luks_erase_key(block, i, writefunc,
+ opaque, errp)) {
+ error_append_hint(errp, "Failed to erase keyslot %zu", i);
+ return -1;
+ }
+ }
+ } else {
+ error_setg(errp,
+ "To erase keyslot(s), either explicit keyslot index "
+ "or the password currently contained in them must be given");
+ return -1;
+ }
return 0;
+}
- error:
- if (masterkey) {
- memset(masterkey, 0, luks->header.key_bytes);
- }
- g_free(masterkey);
- if (slotkey) {
- memset(slotkey, 0, luks->header.key_bytes);
+static int
+qcrypto_block_luks_amend_options(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptions *options,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks;
+
+ switch (opts_luks->state) {
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE:
+ return qcrypto_block_luks_amend_add_keyslot(block, readfunc,
+ writefunc, opaque,
+ opts_luks, force, errp);
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE:
+ return qcrypto_block_luks_amend_erase_keyslots(block, readfunc,
+ writefunc, opaque,
+ opts_luks, force, errp);
+ default:
+ g_assert_not_reached();
}
- g_free(slotkey);
- g_free(splitkey);
- g_free(password);
- g_free(cipher_mode_spec);
-
- qcrypto_ivgen_free(ivgen);
- qcrypto_cipher_free(cipher);
-
- g_free(luks);
- return -1;
}
-
static int qcrypto_block_luks_get_info(QCryptoBlock *block,
QCryptoBlockInfo *info,
Error **errp)
{
QCryptoBlockLUKS *luks = block->opaque;
QCryptoBlockInfoLUKSSlot *slot;
- QCryptoBlockInfoLUKSSlotList *slots = NULL, **prev = &info->u.luks.slots;
+ QCryptoBlockInfoLUKSSlotList **tail = &info->u.luks.slots;
size_t i;
info->u.luks.cipher_alg = luks->cipher_alg;
@@ -1367,15 +1896,13 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
info->u.luks.master_key_iters = luks->header.master_key_iterations;
info->u.luks.uuid = g_strndup((const char *)luks->header.uuid,
sizeof(luks->header.uuid));
+ info->u.luks.detached_header = block->detached_header;
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- slots = g_new0(QCryptoBlockInfoLUKSSlotList, 1);
- *prev = slots;
-
- slots->value = slot = g_new0(QCryptoBlockInfoLUKSSlot, 1);
+ slot = g_new0(QCryptoBlockInfoLUKSSlot, 1);
slot->active = luks->header.key_slots[i].active ==
QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
- slot->key_offset = luks->header.key_slots[i].key_offset
+ slot->key_offset = luks->header.key_slots[i].key_offset_sector
* QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
if (slot->active) {
slot->has_iters = true;
@@ -1384,7 +1911,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
slot->stripes = luks->header.key_slots[i].stripes;
}
- prev = &slots->next;
+ QAPI_LIST_APPEND(tail, slot);
}
return 0;
@@ -1393,7 +1920,11 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
{
- g_free(block->opaque);
+ QCryptoBlockLUKS *luks = block->opaque;
+ if (luks) {
+ g_free(luks->secret);
+ g_free(luks);
+ }
}
@@ -1406,8 +1937,7 @@ qcrypto_block_luks_decrypt(QCryptoBlock *block,
{
assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE));
assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE));
- return qcrypto_block_decrypt_helper(block->cipher,
- block->niv, block->ivgen,
+ return qcrypto_block_decrypt_helper(block,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
offset, buf, len, errp);
}
@@ -1422,8 +1952,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
{
assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE));
assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE));
- return qcrypto_block_encrypt_helper(block->cipher,
- block->niv, block->ivgen,
+ return qcrypto_block_encrypt_helper(block,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
offset, buf, len, errp);
}
@@ -1432,6 +1961,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
const QCryptoBlockDriver qcrypto_block_driver_luks = {
.open = qcrypto_block_luks_open,
.create = qcrypto_block_luks_create,
+ .amend = qcrypto_block_luks_amend_options,
.get_info = qcrypto_block_luks_get_info,
.cleanup = qcrypto_block_luks_cleanup,
.decrypt = qcrypto_block_luks_decrypt,