summaryrefslogtreecommitdiffstats
path: root/lib/luks2
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:31:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:31:19 +0000
commit6e33fee6f4a7e2041dd276995b402ca036fcab14 (patch)
tree85be5c41f2715d7d4d24cfa220197f1e2c778259 /lib/luks2
parentInitial commit. (diff)
downloadcryptsetup-upstream.tar.xz
cryptsetup-upstream.zip
Adding upstream version 2:2.1.0.upstream/2%2.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/luks2')
-rw-r--r--lib/luks2/luks2.h388
-rw-r--r--lib/luks2/luks2_digest.c393
-rw-r--r--lib/luks2/luks2_digest_pbkdf2.c211
-rw-r--r--lib/luks2/luks2_disk_metadata.c769
-rw-r--r--lib/luks2/luks2_internal.h182
-rw-r--r--lib/luks2/luks2_json_format.c311
-rw-r--r--lib/luks2/luks2_json_metadata.c1935
-rw-r--r--lib/luks2/luks2_keyslot.c663
-rw-r--r--lib/luks2/luks2_keyslot_luks2.c785
-rw-r--r--lib/luks2/luks2_luks1_convert.c863
-rw-r--r--lib/luks2/luks2_token.c606
-rw-r--r--lib/luks2/luks2_token_keyring.c170
12 files changed, 7276 insertions, 0 deletions
diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h
new file mode 100644
index 0000000..261d622
--- /dev/null
+++ b/lib/luks2/luks2.h
@@ -0,0 +1,388 @@
+/*
+ * LUKS - Linux Unified Key Setup v2
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef _CRYPTSETUP_LUKS2_ONDISK_H
+#define _CRYPTSETUP_LUKS2_ONDISK_H
+
+#include "libcryptsetup.h"
+
+#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
+#define LUKS2_MAGIC_2ND "SKUL\xba\xbe"
+#define LUKS2_MAGIC_L 6
+#define LUKS2_UUID_L 40
+#define LUKS2_LABEL_L 48
+#define LUKS2_SALT_L 64
+#define LUKS2_CHECKSUM_ALG_L 32
+#define LUKS2_CHECKSUM_L 64
+
+#define LUKS2_KEYSLOTS_MAX 32
+#define LUKS2_TOKENS_MAX 32
+#define LUKS2_SEGMENT_MAX 32
+
+#define LUKS2_BUILTIN_TOKEN_PREFIX "luks2-"
+#define LUKS2_BUILTIN_TOKEN_PREFIX_LEN 6
+
+#define LUKS2_TOKEN_KEYRING LUKS2_BUILTIN_TOKEN_PREFIX "keyring"
+
+#define LUKS2_DIGEST_MAX 8
+
+#define CRYPT_ANY_SEGMENT -1
+#define CRYPT_DEFAULT_SEGMENT 0
+#define CRYPT_DEFAULT_SEGMENT_STR "0"
+
+#define CRYPT_ANY_DIGEST -1
+
+/*
+ * LUKS2 header on-disk.
+ *
+ * Binary header is followed by JSON area.
+ * JSON area is followed by keyslot area and data area,
+ * these are described in JSON metadata.
+ *
+ * Note: uuid, csum_alg are intentionally on the same offset as LUKS1
+ * (checksum alg replaces hash in LUKS1)
+ *
+ * String (char) should be zero terminated.
+ * Padding should be wiped.
+ * Checksum is calculated with csum zeroed (+ full JSON area).
+ */
+struct luks2_hdr_disk {
+ char magic[LUKS2_MAGIC_L];
+ uint16_t version; /* Version 2 */
+ uint64_t hdr_size; /* in bytes, including JSON area */
+ uint64_t seqid; /* increased on every update */
+ char label[LUKS2_LABEL_L];
+ char checksum_alg[LUKS2_CHECKSUM_ALG_L];
+ uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
+ char uuid[LUKS2_UUID_L];
+ char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
+ uint64_t hdr_offset; /* offset from device start in bytes */
+ char _padding[184];
+ uint8_t csum[LUKS2_CHECKSUM_L];
+ char _padding4096[7*512];
+ /* JSON area starts here */
+} __attribute__ ((packed));
+
+/*
+ * LUKS2 header in-memory.
+ */
+typedef struct json_object json_object;
+struct luks2_hdr {
+ size_t hdr_size;
+ uint64_t seqid;
+ unsigned int version;
+ char label[LUKS2_LABEL_L];
+ char subsystem[LUKS2_LABEL_L];
+ char checksum_alg[LUKS2_CHECKSUM_ALG_L];
+ uint8_t salt1[LUKS2_SALT_L];
+ uint8_t salt2[LUKS2_SALT_L];
+ char uuid[LUKS2_UUID_L];
+ json_object *jobj;
+};
+
+struct luks2_keyslot_params {
+ enum { LUKS2_KEYSLOT_AF_LUKS1 = 0 } af_type;
+ enum { LUKS2_KEYSLOT_AREA_RAW = 0 } area_type;
+
+ union {
+ struct {
+ char hash[LUKS2_CHECKSUM_ALG_L]; // or include luks.h
+ unsigned int stripes;
+ } luks1;
+ } af;
+
+ union {
+ struct {
+ char encryption[65]; // or include utils_crypt.h
+ size_t key_size;
+ } raw;
+ } area;
+};
+
+/*
+ * Supportable header sizes (hdr_disk + JSON area)
+ * Also used as offset for the 2nd header.
+ */
+#define LUKS2_HDR_16K_LEN 0x4000
+
+#define LUKS2_HDR_BIN_LEN sizeof(struct luks2_hdr_disk)
+
+//#define LUKS2_DEFAULT_HDR_SIZE 0x400000 /* 4 MiB */
+#define LUKS2_DEFAULT_HDR_SIZE 0x1000000 /* 16 MiB */
+
+#define LUKS2_MAX_KEYSLOTS_SIZE 0x8000000 /* 128 MiB */
+
+#define LUKS2_HDR_OFFSET_MAX 0x400000 /* 4 MiB */
+
+/* Offsets for secondary header (for scan if primary header is corrupted). */
+#define LUKS2_HDR2_OFFSETS { 0x04000, 0x008000, 0x010000, 0x020000, \
+ 0x40000, 0x080000, 0x100000, 0x200000, LUKS2_HDR_OFFSET_MAX }
+
+int LUKS2_hdr_version_unlocked(struct crypt_device *cd,
+ const char *backup_file);
+
+int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair);
+int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr);
+int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr);
+
+int LUKS2_hdr_uuid(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *uuid);
+
+int LUKS2_hdr_labels(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *label,
+ const char *subsystem,
+ int commit);
+
+void LUKS2_hdr_free(struct crypt_device *cd, struct luks2_hdr *hdr);
+
+int LUKS2_hdr_backup(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *backup_file);
+int LUKS2_hdr_restore(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *backup_file);
+
+uint64_t LUKS2_hdr_and_areas_size(json_object *jobj);
+uint64_t LUKS2_keyslots_size(json_object *jobj);
+uint64_t LUKS2_metadata_size(json_object *jobj);
+
+int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *cipher_spec);
+
+/*
+ * Generic LUKS2 keyslot
+ */
+int LUKS2_keyslot_open(struct crypt_device *cd,
+ int keyslot,
+ int segment,
+ const char *password,
+ size_t password_len,
+ struct volume_key **vk);
+
+int LUKS2_keyslot_store(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ const char *password,
+ size_t password_len,
+ const struct volume_key *vk,
+ const struct luks2_keyslot_params *params);
+
+int LUKS2_keyslot_wipe(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int wipe_area_only);
+
+int LUKS2_keyslot_dump(struct crypt_device *cd,
+ int keyslot);
+
+crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot);
+
+int LUKS2_keyslot_priority_set(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ crypt_keyslot_priority priority,
+ int commit);
+
+/*
+ * Generic LUKS2 token
+ */
+int LUKS2_token_json_get(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char **json);
+
+int LUKS2_token_assign(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int token,
+ int assign,
+ int commit);
+
+int LUKS2_token_is_assigned(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int token);
+
+int LUKS2_token_create(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *json,
+ int commit);
+
+crypt_token_info LUKS2_token_status(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char **type);
+
+int LUKS2_builtin_token_get(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *type,
+ void *params);
+
+int LUKS2_builtin_token_create(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *type,
+ const void *params,
+ int commit);
+
+int LUKS2_token_open_and_activate(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *name,
+ uint32_t flags,
+ void *usrptr);
+
+int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *name,
+ uint32_t flags);
+
+int LUKS2_tokens_count(struct luks2_hdr *hdr);
+
+/*
+ * Generic LUKS2 digest
+ */
+int LUKS2_digest_by_segment(struct luks2_hdr *hdr, int segment);
+
+int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int segment,
+ const struct volume_key *vk);
+
+void LUKS2_digests_erase_unused(struct crypt_device *cd,
+ struct luks2_hdr *hdr);
+
+int LUKS2_digest_verify(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ struct volume_key *vk,
+ int keyslot);
+
+int LUKS2_digest_dump(struct crypt_device *cd,
+ int digest);
+
+int LUKS2_digest_assign(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int digest,
+ int assign,
+ int commit);
+
+int LUKS2_digest_segment_assign(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int segment,
+ int digest,
+ int assign,
+ int commit);
+
+int LUKS2_digest_by_keyslot(struct luks2_hdr *hdr, int keyslot);
+
+int LUKS2_digest_create(struct crypt_device *cd,
+ const char *type,
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk);
+
+/*
+ * LUKS2 generic
+ */
+int LUKS2_activate(struct crypt_device *cd,
+ const char *name,
+ struct volume_key *vk,
+ uint32_t flags);
+
+int LUKS2_keyslot_luks2_format(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ const char *cipher,
+ size_t keylength);
+
+int LUKS2_generate_hdr(
+ struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk,
+ const char *cipherName,
+ const char *cipherMode,
+ const char *integrity,
+ const char *uuid,
+ unsigned int sector_size,
+ uint64_t data_offset,
+ uint64_t align_offset,
+ uint64_t required_alignment,
+ uint64_t metadata_size,
+ uint64_t keyslots_size);
+
+int LUKS2_check_metadata_area_size(uint64_t metadata_size);
+int LUKS2_check_keyslots_area_size(uint64_t keyslots_size);
+
+int LUKS2_wipe_header_areas(struct crypt_device *cd,
+ struct luks2_hdr *hdr);
+
+uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr);
+int LUKS2_get_sector_size(struct luks2_hdr *hdr);
+const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment);
+const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment);
+int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr,
+ struct luks2_keyslot_params *params);
+int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment);
+int LUKS2_get_keyslot_stored_key_size(struct luks2_hdr *hdr, int keyslot);
+const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size);
+int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type);
+int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment);
+int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
+crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot);
+int LUKS2_keyslot_area(struct luks2_hdr *hdr,
+ int keyslot,
+ uint64_t *offset,
+ uint64_t *length);
+int LUKS2_keyslot_pbkdf(struct luks2_hdr *hdr, int keyslot, struct crypt_pbkdf_type *pbkdf);
+
+/*
+ * Permanent activation flags stored in header
+ */
+int LUKS2_config_get_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *flags);
+int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t flags);
+
+/*
+ * Requirements for device activation or header modification
+ */
+int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs);
+int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs);
+
+int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet);
+
+int LUKS2_key_description_by_segment(struct crypt_device *cd,
+ struct luks2_hdr *hdr, struct volume_key *vk, int segment);
+int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd,
+ struct luks2_hdr *hdr, struct volume_key *vk, int keyslot);
+
+struct luks_phdr;
+int LUKS2_luks1_to_luks2(struct crypt_device *cd,
+ struct luks_phdr *hdr1,
+ struct luks2_hdr *hdr2);
+int LUKS2_luks2_to_luks1(struct crypt_device *cd,
+ struct luks2_hdr *hdr2,
+ struct luks_phdr *hdr1);
+
+#endif
diff --git a/lib/luks2/luks2_digest.c b/lib/luks2/luks2_digest.c
new file mode 100644
index 0000000..246903c
--- /dev/null
+++ b/lib/luks2/luks2_digest.c
@@ -0,0 +1,393 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, digest handling
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+
+extern const digest_handler PBKDF2_digest;
+
+static const digest_handler *digest_handlers[LUKS2_DIGEST_MAX] = {
+ &PBKDF2_digest,
+ NULL
+};
+
+const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type)
+{
+ int i;
+
+ for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
+ if (!strcmp(digest_handlers[i]->name, type))
+ return digest_handlers[i];
+ }
+
+ return NULL;
+}
+
+static const digest_handler *LUKS2_digest_handler(struct crypt_device *cd, int digest)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj1, *jobj2;
+
+ if (digest < 0)
+ return NULL;
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return NULL;
+
+ if (!(jobj1 = LUKS2_get_digest_jobj(hdr, digest)))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, "type", &jobj2))
+ return NULL;
+
+ return LUKS2_digest_handler_type(cd, json_object_get_string(jobj2));
+}
+
+static int LUKS2_digest_find_free(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+ int digest = 0;
+
+ while (LUKS2_get_digest_jobj(hdr, digest) && digest < LUKS2_DIGEST_MAX)
+ digest++;
+
+ return digest < LUKS2_DIGEST_MAX ? digest : -1;
+}
+
+int LUKS2_digest_create(struct crypt_device *cd,
+ const char *type,
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk)
+{
+ int digest;
+ const digest_handler *dh;
+
+ dh = LUKS2_digest_handler_type(cd, type);
+ if (!dh)
+ return -EINVAL;
+
+ digest = LUKS2_digest_find_free(cd, hdr);
+ if (digest < 0)
+ return -EINVAL;
+
+ log_dbg(cd, "Creating new digest %d (%s).", digest, type);
+
+ return dh->store(cd, digest, vk->key, vk->keylength) ?: digest;
+}
+
+int LUKS2_digest_by_keyslot(struct luks2_hdr *hdr, int keyslot)
+{
+ char keyslot_name[16];
+ json_object *jobj_digests, *jobj_digest_keyslots;
+
+ if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
+ return -ENOMEM;
+
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ json_object_object_get_ex(val, "keyslots", &jobj_digest_keyslots);
+ if (LUKS2_array_jobj(jobj_digest_keyslots, keyslot_name))
+ return atoi(key);
+ }
+
+ return -ENOENT;
+}
+
+int LUKS2_digest_verify(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ struct volume_key *vk,
+ int keyslot)
+{
+ const digest_handler *h;
+ int digest, r;
+
+ digest = LUKS2_digest_by_keyslot(hdr, keyslot);
+ if (digest < 0)
+ return digest;
+
+ log_dbg(cd, "Verifying key from keyslot %d, digest %d.", keyslot, digest);
+ h = LUKS2_digest_handler(cd, digest);
+ if (!h)
+ return -EINVAL;
+
+ r = h->verify(cd, digest, vk->key, vk->keylength);
+ if (r < 0) {
+ log_dbg(cd, "Digest %d (%s) verify failed with %d.", digest, h->name, r);
+ return r;
+ }
+
+ return digest;
+}
+
+int LUKS2_digest_dump(struct crypt_device *cd, int digest)
+{
+ const digest_handler *h;
+
+ if (!(h = LUKS2_digest_handler(cd, digest)))
+ return -EINVAL;
+
+ return h->dump(cd, digest);
+}
+
+int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int segment,
+ const struct volume_key *vk)
+{
+ const digest_handler *h;
+ int digest, r;
+
+ digest = LUKS2_digest_by_segment(hdr, segment);
+ if (digest < 0)
+ return digest;
+
+ log_dbg(cd, "Verifying key digest %d.", digest);
+
+ h = LUKS2_digest_handler(cd, digest);
+ if (!h)
+ return -EINVAL;
+
+ r = h->verify(cd, digest, vk->key, vk->keylength);
+ if (r < 0) {
+ log_dbg(cd, "Digest %d (%s) verify failed with %d.", digest, h->name, r);
+ return r;
+ }
+
+ return digest;
+}
+
+/* FIXME: segment can have more digests */
+int LUKS2_digest_by_segment(struct luks2_hdr *hdr, int segment)
+{
+ char segment_name[16];
+ json_object *jobj_digests, *jobj_digest_segments;
+
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+
+ if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
+ return -EINVAL;
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ json_object_object_get_ex(val, "segments", &jobj_digest_segments);
+ if (!LUKS2_array_jobj(jobj_digest_segments, segment_name))
+ continue;
+
+ return atoi(key);
+ }
+
+ return -ENOENT;
+}
+
+static int assign_one_digest(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, int digest, int assign)
+{
+ json_object *jobj1, *jobj_digest, *jobj_digest_keyslots;
+ char num[16];
+
+ log_dbg(cd, "Keyslot %i %s digest %i.", keyslot, assign ? "assigned to" : "unassigned from", digest);
+
+ jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
+ if (!jobj_digest)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_digest, "keyslots", &jobj_digest_keyslots);
+ if (!jobj_digest_keyslots)
+ return -EINVAL;
+
+ snprintf(num, sizeof(num), "%d", keyslot);
+ if (assign) {
+ jobj1 = LUKS2_array_jobj(jobj_digest_keyslots, num);
+ if (!jobj1)
+ json_object_array_add(jobj_digest_keyslots, json_object_new_string(num));
+ } else {
+ jobj1 = LUKS2_array_remove(jobj_digest_keyslots, num);
+ if (jobj1)
+ json_object_object_add(jobj_digest, "keyslots", jobj1);
+ }
+
+ return 0;
+}
+
+int LUKS2_digest_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, int digest, int assign, int commit)
+{
+ json_object *jobj_digests;
+ int r = 0;
+
+ if (digest == CRYPT_ANY_DIGEST) {
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ UNUSED(val);
+ r = assign_one_digest(cd, hdr, keyslot, atoi(key), assign);
+ if (r < 0)
+ break;
+ }
+ } else
+ r = assign_one_digest(cd, hdr, keyslot, digest, assign);
+
+ if (r < 0)
+ return r;
+
+ // FIXME: do not write header in nothing changed
+ return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+}
+
+static int assign_one_segment(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int segment, int digest, int assign)
+{
+ json_object *jobj1, *jobj_digest, *jobj_digest_segments;
+ char num[16];
+
+ log_dbg(cd, "Segment %i %s digest %i.", segment, assign ? "assigned to" : "unassigned from", digest);
+
+ jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
+ if (!jobj_digest)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_digest, "segments", &jobj_digest_segments);
+ if (!jobj_digest_segments)
+ return -EINVAL;
+
+ snprintf(num, sizeof(num), "%d", segment);
+ if (assign) {
+ jobj1 = LUKS2_array_jobj(jobj_digest_segments, num);
+ if (!jobj1)
+ json_object_array_add(jobj_digest_segments, json_object_new_string(num));
+ } else {
+ jobj1 = LUKS2_array_remove(jobj_digest_segments, num);
+ if (jobj1)
+ json_object_object_add(jobj_digest, "segments", jobj1);
+ }
+
+ return 0;
+}
+
+int LUKS2_digest_segment_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int segment, int digest, int assign, int commit)
+{
+ json_object *jobj_digests;
+ int r = 0;
+
+ if (digest == CRYPT_ANY_DIGEST) {
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ UNUSED(val);
+ r = assign_one_segment(cd, hdr, segment, atoi(key), assign);
+ if (r < 0)
+ break;
+ }
+ } else
+ r = assign_one_segment(cd, hdr, segment, digest, assign);
+
+ if (r < 0)
+ return r;
+
+ // FIXME: do not write header in nothing changed
+ return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+}
+
+static int digest_unused(json_object *jobj_digest)
+{
+ json_object *jobj;
+
+ json_object_object_get_ex(jobj_digest, "segments", &jobj);
+ if (!jobj || !json_object_is_type(jobj, json_type_array) || json_object_array_length(jobj) > 0)
+ return 0;
+
+ json_object_object_get_ex(jobj_digest, "keyslots", &jobj);
+ if (!jobj || !json_object_is_type(jobj, json_type_array))
+ return 0;
+
+ return json_object_array_length(jobj) > 0 ? 0 : 1;
+}
+
+void LUKS2_digests_erase_unused(struct crypt_device *cd,
+ struct luks2_hdr *hdr)
+{
+ json_object *jobj_digests;
+
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+ if (!jobj_digests || !json_object_is_type(jobj_digests, json_type_object))
+ return;
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ if (digest_unused(val)) {
+ log_dbg(cd, "Erasing unused digest %d.", atoi(key));
+ json_object_object_del(jobj_digests, key);
+ }
+ }
+}
+
+/* Key description helpers */
+static char *get_key_description_by_digest(struct crypt_device *cd, int digest)
+{
+ char *desc, digest_str[3];
+ int r;
+ size_t len;
+
+ if (!crypt_get_uuid(cd))
+ return NULL;
+
+ r = snprintf(digest_str, sizeof(digest_str), "d%u", digest);
+ if (r < 0 || (size_t)r >= sizeof(digest_str))
+ return NULL;
+
+ /* "cryptsetup:<uuid>-<digest_str>" + \0 */
+ len = strlen(crypt_get_uuid(cd)) + strlen(digest_str) + 13;
+
+ desc = malloc(len);
+ if (!desc)
+ return NULL;
+
+ r = snprintf(desc, len, "%s:%s-%s", "cryptsetup", crypt_get_uuid(cd), digest_str);
+ if (r < 0 || (size_t)r >= len) {
+ free(desc);
+ return NULL;
+ }
+
+ return desc;
+}
+
+int LUKS2_key_description_by_segment(struct crypt_device *cd,
+ struct luks2_hdr *hdr, struct volume_key *vk, int segment)
+{
+ char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_segment(hdr, segment));
+ int r;
+
+ r = crypt_volume_key_set_description(vk, desc);
+ free(desc);
+ return r;
+}
+
+int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd,
+ struct luks2_hdr *hdr, struct volume_key *vk, int keyslot)
+{
+ char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_keyslot(hdr, keyslot));
+ int r;
+
+ r = crypt_volume_key_set_description(vk, desc);
+ if (!r)
+ r = crypt_volume_key_load_in_keyring(cd, vk);
+
+ free(desc);
+ return r;
+}
diff --git a/lib/luks2/luks2_digest_pbkdf2.c b/lib/luks2/luks2_digest_pbkdf2.c
new file mode 100644
index 0000000..e4413ab
--- /dev/null
+++ b/lib/luks2/luks2_digest_pbkdf2.c
@@ -0,0 +1,211 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+
+#define LUKS_DIGESTSIZE 20 // since SHA1
+#define LUKS_SALTSIZE 32
+#define LUKS_MKD_ITERATIONS_MS 125
+
+static int PBKDF2_digest_verify(struct crypt_device *cd,
+ int digest,
+ const char *volume_key,
+ size_t volume_key_len)
+{
+ char checkHashBuf[64];
+ json_object *jobj_digest, *jobj1;
+ const char *hashSpec;
+ char *mkDigest = NULL, mkDigestSalt[LUKS_SALTSIZE];
+ unsigned int mkDigestIterations;
+ size_t len;
+ int r;
+
+ /* This can be done only for internally linked digests */
+ jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
+ if (!jobj_digest)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
+ return -EINVAL;
+ hashSpec = json_object_get_string(jobj1);
+
+ if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
+ return -EINVAL;
+ mkDigestIterations = json_object_get_int64(jobj1);
+
+ if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
+ return -EINVAL;
+ len = sizeof(mkDigestSalt);
+ if (!base64_decode(json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1), mkDigestSalt, &len))
+ return -EINVAL;
+ if (len != LUKS_SALTSIZE)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
+ return -EINVAL;
+ len = 0;
+ if (!base64_decode_alloc(json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1), &mkDigest, &len))
+ return -EINVAL;
+ if (len < LUKS_DIGESTSIZE ||
+ len > sizeof(checkHashBuf) ||
+ (len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec))) {
+ free(mkDigest);
+ return -EINVAL;
+ }
+
+ r = -EPERM;
+ if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
+ mkDigestSalt, LUKS_SALTSIZE,
+ checkHashBuf, len,
+ mkDigestIterations, 0, 0) < 0) {
+ r = -EINVAL;
+ } else {
+ if (memcmp(checkHashBuf, mkDigest, len) == 0)
+ r = 0;
+ }
+
+ free(mkDigest);
+ return r;
+}
+
+static int PBKDF2_digest_store(struct crypt_device *cd,
+ int digest,
+ const char *volume_key,
+ size_t volume_key_len)
+{
+ json_object *jobj_digest, *jobj_digests;
+ char salt[LUKS_SALTSIZE], digest_raw[128];
+ int hmac_size, r;
+ char *base64_str;
+ struct luks2_hdr *hdr;
+ struct crypt_pbkdf_limits pbkdf_limits;
+ const struct crypt_pbkdf_type *pbkdf_cd;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_PBKDF2,
+ .time_ms = LUKS_MKD_ITERATIONS_MS,
+ };
+
+ /* Inherit hash from PBKDF setting */
+ pbkdf_cd = crypt_get_pbkdf_type(cd);
+ if (pbkdf_cd)
+ pbkdf.hash = pbkdf_cd->hash;
+ if (!pbkdf.hash)
+ pbkdf.hash = DEFAULT_LUKS1_HASH;
+
+ log_dbg(cd, "Setting PBKDF2 type key digest %d.", digest);
+
+ r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
+ if (r < 0)
+ return r;
+
+ r = crypt_pbkdf_get_limits(CRYPT_KDF_PBKDF2, &pbkdf_limits);
+ if (r < 0)
+ return r;
+
+ if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
+ pbkdf.iterations = pbkdf_limits.min_iterations;
+ else {
+ r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
+ if (r < 0)
+ return r;
+ }
+
+ hmac_size = crypt_hmac_size(pbkdf.hash);
+ if (hmac_size < 0)
+ return hmac_size;
+
+ r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
+ salt, LUKS_SALTSIZE, digest_raw, hmac_size,
+ pbkdf.iterations, 0, 0);
+ if (r < 0)
+ return r;
+
+ jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
+ jobj_digests = NULL;
+ if (!jobj_digest) {
+ hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ jobj_digest = json_object_new_object();
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+ }
+
+ json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
+ json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
+ json_object_object_add(jobj_digest, "segments", json_object_new_array());
+ json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
+ json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
+
+ base64_encode_alloc(salt, LUKS_SALTSIZE, &base64_str);
+ if (!base64_str) {
+ json_object_put(jobj_digest);
+ return -ENOMEM;
+ }
+ json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
+ free(base64_str);
+
+ base64_encode_alloc(digest_raw, hmac_size, &base64_str);
+ if (!base64_str) {
+ json_object_put(jobj_digest);
+ return -ENOMEM;
+ }
+ json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
+ free(base64_str);
+
+ if (jobj_digests)
+ json_object_object_add_by_uint(jobj_digests, digest, jobj_digest);
+
+ JSON_DBG(cd, jobj_digest, "Digest JSON:");
+ return 0;
+}
+
+static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
+{
+ json_object *jobj_digest, *jobj1;
+
+ /* This can be done only for internally linked digests */
+ jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
+ if (!jobj_digest)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_digest, "hash", &jobj1);
+ log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
+
+ json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
+ log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
+
+ json_object_object_get_ex(jobj_digest, "salt", &jobj1);
+ log_std(cd, "\tSalt: ");
+ hexprint_base64(cd, jobj1, " ", " ");
+
+ json_object_object_get_ex(jobj_digest, "digest", &jobj1);
+ log_std(cd, "\tDigest: ");
+ hexprint_base64(cd, jobj1, " ", " ");
+
+ return 0;
+}
+
+const digest_handler PBKDF2_digest = {
+ .name = "pbkdf2",
+ .verify = PBKDF2_digest_verify,
+ .store = PBKDF2_digest_store,
+ .dump = PBKDF2_digest_dump,
+};
diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c
new file mode 100644
index 0000000..c6a121a
--- /dev/null
+++ b/lib/luks2/luks2_disk_metadata.c
@@ -0,0 +1,769 @@
+/*
+ * LUKS - Linux Unified Key Setup v2
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 <assert.h>
+
+#include "luks2_internal.h"
+
+/*
+ * Helper functions
+ */
+json_object *parse_json_len(struct crypt_device *cd, const char *json_area,
+ uint64_t max_length, int *json_len)
+{
+ json_object *jobj;
+ struct json_tokener *jtok;
+
+ /* INT32_MAX is internal (json-c) json_tokener_parse_ex() limit */
+ if (!json_area || max_length > INT32_MAX)
+ return NULL;
+
+ jtok = json_tokener_new();
+ if (!jtok) {
+ log_dbg(cd, "ERROR: Failed to init json tokener");
+ return NULL;
+ }
+
+ jobj = json_tokener_parse_ex(jtok, json_area, max_length);
+ if (!jobj)
+ log_dbg(cd, "ERROR: Failed to parse json data (%d): %s",
+ json_tokener_get_error(jtok),
+ json_tokener_error_desc(json_tokener_get_error(jtok)));
+ else
+ *json_len = jtok->char_offset;
+
+ json_tokener_free(jtok);
+
+ return jobj;
+}
+
+static void log_dbg_checksum(struct crypt_device *cd,
+ const uint8_t *csum, const char *csum_alg, const char *info)
+{
+ char csum_txt[2*LUKS2_CHECKSUM_L+1];
+ int i;
+
+ for (i = 0; i < crypt_hash_size(csum_alg); i++)
+ snprintf(&csum_txt[i*2], 3, "%02hhx", (const char)csum[i]);
+ csum_txt[i*2+1] = '\0'; /* Just to be safe, sprintf should write \0 there. */
+
+ log_dbg(cd, "Checksum:%s (%s)", &csum_txt[0], info);
+}
+
+/*
+ * Calculate hash (checksum) of |LUKS2_bin|LUKS2_JSON_area| from in-memory structs.
+ * LUKS2 on-disk header contains uniques salt both for primary and secondary header.
+ * Checksum is always calculated with zeroed checksum field in binary header.
+ */
+static int hdr_checksum_calculate(const char *alg, struct luks2_hdr_disk *hdr_disk,
+ const char *json_area, size_t json_len)
+{
+ struct crypt_hash *hd = NULL;
+ int hash_size, r;
+
+ hash_size = crypt_hash_size(alg);
+ if (hash_size <= 0 || crypt_hash_init(&hd, alg))
+ return -EINVAL;
+
+ /* Binary header, csum zeroed. */
+ r = crypt_hash_write(hd, (char*)hdr_disk, LUKS2_HDR_BIN_LEN);
+
+ /* JSON area (including unused space) */
+ if (!r)
+ r = crypt_hash_write(hd, json_area, json_len);
+
+ if (!r)
+ r = crypt_hash_final(hd, (char*)hdr_disk->csum, (size_t)hash_size);
+
+ crypt_hash_destroy(hd);
+ return r;
+}
+
+/*
+ * Compare hash (checksum) of on-disk and in-memory header.
+ */
+static int hdr_checksum_check(struct crypt_device *cd,
+ const char *alg, struct luks2_hdr_disk *hdr_disk,
+ const char *json_area, size_t json_len)
+{
+ struct luks2_hdr_disk hdr_tmp;
+ int hash_size, r;
+
+ hash_size = crypt_hash_size(alg);
+ if (hash_size <= 0)
+ return -EINVAL;
+
+ /* Copy header and zero checksum. */
+ memcpy(&hdr_tmp, hdr_disk, LUKS2_HDR_BIN_LEN);
+ memset(&hdr_tmp.csum, 0, sizeof(hdr_tmp.csum));
+
+ r = hdr_checksum_calculate(alg, &hdr_tmp, json_area, json_len);
+ if (r < 0)
+ return r;
+
+ log_dbg_checksum(cd, hdr_disk->csum, alg, "on-disk");
+ log_dbg_checksum(cd, hdr_tmp.csum, alg, "in-memory");
+
+ if (memcmp(hdr_tmp.csum, hdr_disk->csum, (size_t)hash_size))
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Convert header from on-disk format to in-memory struct
+ */
+static void hdr_from_disk(struct luks2_hdr_disk *hdr_disk1,
+ struct luks2_hdr_disk *hdr_disk2,
+ struct luks2_hdr *hdr,
+ int secondary)
+{
+ hdr->version = be16_to_cpu(hdr_disk1->version);
+ hdr->hdr_size = be64_to_cpu(hdr_disk1->hdr_size);
+ hdr->seqid = be64_to_cpu(hdr_disk1->seqid);
+
+ memcpy(hdr->label, hdr_disk1->label, LUKS2_LABEL_L);
+ hdr->label[LUKS2_LABEL_L - 1] = '\0';
+ memcpy(hdr->subsystem, hdr_disk1->subsystem, LUKS2_LABEL_L);
+ hdr->subsystem[LUKS2_LABEL_L - 1] = '\0';
+ memcpy(hdr->checksum_alg, hdr_disk1->checksum_alg, LUKS2_CHECKSUM_ALG_L);
+ hdr->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
+ memcpy(hdr->uuid, hdr_disk1->uuid, LUKS2_UUID_L);
+ hdr->uuid[LUKS2_UUID_L - 1] = '\0';
+
+ if (secondary) {
+ memcpy(hdr->salt1, hdr_disk2->salt, LUKS2_SALT_L);
+ memcpy(hdr->salt2, hdr_disk1->salt, LUKS2_SALT_L);
+ } else {
+ memcpy(hdr->salt1, hdr_disk1->salt, LUKS2_SALT_L);
+ memcpy(hdr->salt2, hdr_disk2->salt, LUKS2_SALT_L);
+ }
+}
+
+/*
+ * Convert header from in-memory struct to on-disk format
+ */
+static void hdr_to_disk(struct luks2_hdr *hdr,
+ struct luks2_hdr_disk *hdr_disk,
+ int secondary, uint64_t offset)
+{
+ assert(((char*)&(hdr_disk->_padding4096) - (char*)&(hdr_disk->magic)) == 512);
+
+ memset(hdr_disk, 0, LUKS2_HDR_BIN_LEN);
+
+ memcpy(&hdr_disk->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
+ hdr_disk->version = cpu_to_be16(hdr->version);
+ hdr_disk->hdr_size = cpu_to_be64(hdr->hdr_size);
+ hdr_disk->hdr_offset = cpu_to_be64(offset);
+ hdr_disk->seqid = cpu_to_be64(hdr->seqid);
+
+ strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L);
+ hdr_disk->label[LUKS2_LABEL_L - 1] = '\0';
+ strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L);
+ hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0';
+ strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L);
+ hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
+ strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L);
+ hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0';
+
+ memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L);
+}
+
+/*
+ * Sanity checks before checksum is validated
+ */
+static int hdr_disk_sanity_check_pre(struct crypt_device *cd,
+ struct luks2_hdr_disk *hdr,
+ size_t *hdr_json_size, int secondary,
+ uint64_t offset)
+{
+ if (memcmp(hdr->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
+ return -EINVAL;
+
+ if (be16_to_cpu(hdr->version) != 2) {
+ log_dbg(cd, "Unsupported LUKS2 header version %u.", be16_to_cpu(hdr->version));
+ return -EINVAL;
+ }
+
+ if (offset != be64_to_cpu(hdr->hdr_offset)) {
+ log_dbg(cd, "LUKS2 offset 0x%04x on device differs to expected offset 0x%04x.",
+ (unsigned)be64_to_cpu(hdr->hdr_offset), (unsigned)offset);
+ return -EINVAL;
+ }
+
+ if (secondary && (offset != be64_to_cpu(hdr->hdr_size))) {
+ log_dbg(cd, "LUKS2 offset 0x%04x in secondary header doesn't match size 0x%04x.",
+ (unsigned)offset, (unsigned)be64_to_cpu(hdr->hdr_size));
+ return -EINVAL;
+ }
+
+ /* FIXME: sanity check checksum alg. */
+
+ log_dbg(cd, "LUKS2 header version %u of size %u bytes, checksum %s.",
+ (unsigned)be16_to_cpu(hdr->version), (unsigned)be64_to_cpu(hdr->hdr_size),
+ hdr->checksum_alg);
+
+ *hdr_json_size = be64_to_cpu(hdr->hdr_size) - LUKS2_HDR_BIN_LEN;
+ return 0;
+}
+
+/*
+ * Read LUKS2 header from disk at specific offset.
+ */
+static int hdr_read_disk(struct crypt_device *cd,
+ struct device *device, struct luks2_hdr_disk *hdr_disk,
+ char **json_area, uint64_t offset, int secondary)
+{
+ size_t hdr_json_size = 0;
+ int devfd = -1, r;
+
+ log_dbg(cd, "Trying to read %s LUKS2 header at offset 0x%" PRIx64 ".",
+ secondary ? "secondary" : "primary", offset);
+
+ devfd = device_open_locked(cd, device, O_RDONLY);
+ if (devfd < 0)
+ return devfd == -1 ? -EIO : devfd;
+
+ /*
+ * Read binary header and run sanity check before reading
+ * JSON area and validating checksum.
+ */
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), hdr_disk,
+ LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) {
+ close(devfd);
+ return -EIO;
+ }
+
+ r = hdr_disk_sanity_check_pre(cd, hdr_disk, &hdr_json_size, secondary, offset);
+ if (r < 0) {
+ close(devfd);
+ return r;
+ }
+
+ /*
+ * Allocate and read JSON area. Always the whole area must be read.
+ */
+ *json_area = malloc(hdr_json_size);
+ if (!*json_area) {
+ close(devfd);
+ return -ENOMEM;
+ }
+
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), *json_area, hdr_json_size,
+ offset + LUKS2_HDR_BIN_LEN) != (ssize_t)hdr_json_size) {
+ close(devfd);
+ free(*json_area);
+ *json_area = NULL;
+ return -EIO;
+ }
+
+ close(devfd);
+
+ /*
+ * Calculate and validate checksum and zero it afterwards.
+ */
+ if (hdr_checksum_check(cd, hdr_disk->checksum_alg, hdr_disk,
+ *json_area, hdr_json_size)) {
+ log_dbg(cd, "LUKS2 header checksum error (offset %" PRIu64 ").", offset);
+ r = -EINVAL;
+ }
+ memset(hdr_disk->csum, 0, LUKS2_CHECKSUM_L);
+
+ return r;
+}
+
+/*
+ * Write LUKS2 header to disk at specific offset.
+ */
+static int hdr_write_disk(struct crypt_device *cd,
+ struct device *device, struct luks2_hdr *hdr,
+ const char *json_area, int secondary)
+{
+ struct luks2_hdr_disk hdr_disk;
+ uint64_t offset = secondary ? hdr->hdr_size : 0;
+ size_t hdr_json_len;
+ int devfd = -1, r;
+
+ log_dbg(cd, "Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".",
+ hdr->hdr_size, offset);
+
+ /* FIXME: read-only device silent fail? */
+
+ devfd = device_open_locked(cd, device, O_RDWR);
+ if (devfd < 0)
+ return devfd == -1 ? -EINVAL : devfd;
+
+ hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
+
+ hdr_to_disk(hdr, &hdr_disk, secondary, offset);
+
+ /*
+ * Write header without checksum but with proper seqid.
+ */
+ if (write_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), (char *)&hdr_disk,
+ LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) {
+ close(devfd);
+ return -EIO;
+ }
+
+ /*
+ * Write json area.
+ */
+ if (write_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device),
+ CONST_CAST(char*)json_area, hdr_json_len,
+ LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) {
+ close(devfd);
+ return -EIO;
+ }
+
+ /*
+ * Calculate checksum and write header with checksum.
+ */
+ r = hdr_checksum_calculate(hdr_disk.checksum_alg, &hdr_disk,
+ json_area, hdr_json_len);
+ if (r < 0) {
+ close(devfd);
+ return r;
+ }
+ log_dbg_checksum(cd, hdr_disk.csum, hdr_disk.checksum_alg, "in-memory");
+
+ if (write_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), (char *)&hdr_disk,
+ LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN)
+ r = -EIO;
+
+ device_sync(cd, device, devfd);
+ close(devfd);
+ return r;
+}
+
+/*
+ * Convert in-memory LUKS2 header and write it to disk.
+ * This will increase sequence id, write both header copies and calculate checksum.
+ */
+int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct device *device)
+{
+ char *json_area;
+ const char *json_text;
+ size_t json_area_len;
+ int r;
+
+ if (hdr->version != 2) {
+ log_dbg(cd, "Unsupported LUKS2 header version (%u).", hdr->version);
+ return -EINVAL;
+ }
+
+ r = device_check_size(cd, crypt_metadata_device(cd), LUKS2_hdr_and_areas_size(hdr->jobj), 1);
+ if (r)
+ return r;
+
+ /*
+ * Allocate and zero JSON area (of proper header size).
+ */
+ json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
+ json_area = malloc(json_area_len);
+ if (!json_area)
+ return -ENOMEM;
+ memset(json_area, 0, json_area_len);
+
+ /*
+ * Generate text space-efficient JSON representation to json area.
+ */
+ json_text = json_object_to_json_string_ext(hdr->jobj,
+ JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
+ if (!json_text || !*json_text) {
+ log_dbg(cd, "Cannot parse JSON object to text representation.");
+ free(json_area);
+ return -ENOMEM;
+ }
+ if (strlen(json_text) > (json_area_len - 1)) {
+ log_dbg(cd, "JSON is too large (%zu > %zu).", strlen(json_text), json_area_len);
+ free(json_area);
+ return -EINVAL;
+ }
+ strncpy(json_area, json_text, json_area_len);
+
+ /* Increase sequence id before writing it to disk. */
+ hdr->seqid++;
+
+ r = device_write_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire write device lock."));
+ free(json_area);
+ return r;
+ }
+
+ /* Write primary and secondary header */
+ r = hdr_write_disk(cd, device, hdr, json_area, 0);
+ if (!r)
+ r = hdr_write_disk(cd, device, hdr, json_area, 1);
+
+ if (r)
+ log_dbg(cd, "LUKS2 header write failed (%d).", r);
+
+ device_write_unlock(cd, device);
+
+ /* FIXME: try recovery here? */
+
+ free(json_area);
+ return r;
+}
+static int validate_json_area(struct crypt_device *cd, const char *json_area,
+ uint64_t json_len, uint64_t max_length)
+{
+ char c;
+
+ /* Enforce there are no needless opening bytes */
+ if (*json_area != '{') {
+ log_dbg(cd, "ERROR: Opening character must be left curly bracket: '{'.");
+ return -EINVAL;
+ }
+
+ if (json_len >= max_length) {
+ log_dbg(cd, "ERROR: Missing trailing null byte beyond parsed json data string.");
+ return -EINVAL;
+ }
+
+ /*
+ * TODO:
+ * validate there are legal json format characters between
+ * 'json_area' and 'json_area + json_len'
+ */
+
+ do {
+ c = *(json_area + json_len);
+ if (c != '\0') {
+ log_dbg(cd, "ERROR: Forbidden ascii code 0x%02hhx found beyond json data string at offset %" PRIu64,
+ c, json_len);
+ return -EINVAL;
+ }
+ } while (++json_len < max_length);
+
+ return 0;
+}
+
+static int validate_luks2_json_object(struct crypt_device *cd, json_object *jobj_hdr, uint64_t length)
+{
+ int r;
+
+ /* we require top level object to be of json_type_object */
+ r = !json_object_is_type(jobj_hdr, json_type_object);
+ if (r) {
+ log_dbg(cd, "ERROR: Resulting object is not a json object type");
+ return r;
+ }
+
+ r = LUKS2_hdr_validate(cd, jobj_hdr, length);
+ if (r) {
+ log_dbg(cd, "Repairing JSON metadata.");
+ /* try to correct known glitches */
+ LUKS2_hdr_repair(cd, jobj_hdr);
+
+ /* run validation again */
+ r = LUKS2_hdr_validate(cd, jobj_hdr, length);
+ }
+
+ if (r)
+ log_dbg(cd, "ERROR: LUKS2 validation failed");
+
+ return r;
+}
+
+static json_object *parse_and_validate_json(struct crypt_device *cd,
+ const char *json_area, uint64_t max_length)
+{
+ int json_len, r;
+ json_object *jobj = parse_json_len(cd, json_area, max_length, &json_len);
+
+ if (!jobj)
+ return NULL;
+
+ /* successful parse_json_len must not return offset <= 0 */
+ assert(json_len > 0);
+
+ r = validate_json_area(cd, json_area, json_len, max_length);
+ if (!r)
+ r = validate_luks2_json_object(cd, jobj, max_length);
+
+ if (r) {
+ json_object_put(jobj);
+ jobj = NULL;
+ }
+
+ return jobj;
+}
+
+static int detect_device_signatures(struct crypt_device *cd, const char *path)
+{
+ blk_probe_status prb_state;
+ int r;
+ struct blkid_handle *h;
+
+ if (!blk_supported()) {
+ log_dbg(cd, "Blkid probing of device signatures disabled.");
+ return 0;
+ }
+
+ if ((r = blk_init_by_path(&h, path))) {
+ log_dbg(cd, "Failed to initialize blkid_handle by path.");
+ return -EINVAL;
+ }
+
+ /* We don't care about details. Be fast. */
+ blk_set_chains_for_fast_detection(h);
+
+ /* Filter out crypto_LUKS. we don't care now */
+ blk_superblocks_filter_luks(h);
+
+ prb_state = blk_safeprobe(h);
+
+ switch (prb_state) {
+ case PRB_AMBIGUOUS:
+ log_dbg(cd, "Blkid probe couldn't decide device type unambiguously.");
+ /* fall through */
+ case PRB_FAIL:
+ log_dbg(cd, "Blkid probe failed.");
+ r = -EINVAL;
+ break;
+ case PRB_OK: /* crypto_LUKS type is filtered out */
+ r = -EINVAL;
+
+ if (blk_is_partition(h))
+ log_dbg(cd, "Blkid probe detected partition type '%s'", blk_get_partition_type(h));
+ else if (blk_is_superblock(h))
+ log_dbg(cd, "blkid probe detected superblock type '%s'", blk_get_superblock_type(h));
+ break;
+ case PRB_EMPTY:
+ log_dbg(cd, "Blkid probe detected no foreign device signature.");
+ }
+ blk_free(h);
+ return r;
+}
+
+/*
+ * Read and convert on-disk LUKS2 header to in-memory representation..
+ * Try to do recovery if on-disk state is not consistent.
+ */
+int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
+ struct device *device, int do_recovery, int do_blkprobe)
+{
+ enum { HDR_OK, HDR_OBSOLETE, HDR_FAIL, HDR_FAIL_IO } state_hdr1, state_hdr2;
+ struct luks2_hdr_disk hdr_disk1, hdr_disk2;
+ char *json_area1 = NULL, *json_area2 = NULL;
+ json_object *jobj_hdr1 = NULL, *jobj_hdr2 = NULL;
+ unsigned int i;
+ int r;
+ uint64_t hdr_size;
+ uint64_t hdr2_offsets[] = LUKS2_HDR2_OFFSETS;
+
+ /* Skip auto-recovery if locks are disabled and we're not doing LUKS2 explicit repair */
+ if (do_recovery && do_blkprobe && !crypt_metadata_locking_enabled()) {
+ do_recovery = 0;
+ log_dbg(cd, "Disabling header auto-recovery due to locking being disabled.");
+ }
+
+ /*
+ * Read primary LUKS2 header (offset 0).
+ */
+ state_hdr1 = HDR_FAIL;
+ r = hdr_read_disk(cd, device, &hdr_disk1, &json_area1, 0, 0);
+ if (r == 0) {
+ jobj_hdr1 = parse_and_validate_json(cd, json_area1, be64_to_cpu(hdr_disk1.hdr_size) - LUKS2_HDR_BIN_LEN);
+ state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE;
+ } else if (r == -EIO)
+ state_hdr1 = HDR_FAIL_IO;
+
+ /*
+ * Read secondary LUKS2 header (follows primary).
+ */
+ state_hdr2 = HDR_FAIL;
+ if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) {
+ r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1);
+ if (r == 0) {
+ jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
+ state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
+ } else if (r == -EIO)
+ state_hdr2 = HDR_FAIL_IO;
+ } else {
+ /*
+ * No header size, check all known offsets.
+ */
+ for (r = -EINVAL,i = 0; r < 0 && i < ARRAY_SIZE(hdr2_offsets); i++)
+ r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, hdr2_offsets[i], 1);
+
+ if (r == 0) {
+ jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
+ state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
+ } else if (r == -EIO)
+ state_hdr2 = HDR_FAIL_IO;
+ }
+
+ /*
+ * Check sequence id if both headers are read correctly.
+ */
+ if (state_hdr1 == HDR_OK && state_hdr2 == HDR_OK) {
+ if (be64_to_cpu(hdr_disk1.seqid) > be64_to_cpu(hdr_disk2.seqid))
+ state_hdr2 = HDR_OBSOLETE;
+ else if (be64_to_cpu(hdr_disk1.seqid) < be64_to_cpu(hdr_disk2.seqid))
+ state_hdr1 = HDR_OBSOLETE;
+ }
+
+ /* check header with keyslots to fit the device */
+ if (state_hdr1 == HDR_OK)
+ hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr1);
+ else if (state_hdr2 == HDR_OK)
+ hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr2);
+ else {
+ r = (state_hdr1 == HDR_FAIL_IO && state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
+ goto err;
+ }
+
+ r = device_check_size(cd, device, hdr_size, 0);
+ if (r)
+ goto err;
+
+ /*
+ * Try to rewrite (recover) bad header. Always regenerate salt for bad header.
+ */
+ if (state_hdr1 == HDR_OK && state_hdr2 != HDR_OK) {
+ log_dbg(cd, "Secondary LUKS2 header requires recovery.");
+
+ if (do_blkprobe && (r = detect_device_signatures(cd, device_path(device)))) {
+ log_err(cd, _("Device contains ambiguous signatures, cannot auto-recover LUKS2.\n"
+ "Please run \"cryptsetup repair\" for recovery."));
+ goto err;
+ }
+
+ if (do_recovery) {
+ memcpy(&hdr_disk2, &hdr_disk1, LUKS2_HDR_BIN_LEN);
+ r = crypt_random_get(cd, (char*)hdr_disk2.salt, sizeof(hdr_disk2.salt), CRYPT_RND_SALT);
+ if (r)
+ log_dbg(cd, "Cannot generate master salt.");
+ else {
+ hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
+ r = hdr_write_disk(cd, device, hdr, json_area1, 1);
+ }
+ if (r)
+ log_dbg(cd, "Secondary LUKS2 header recovery failed.");
+ }
+ } else if (state_hdr1 != HDR_OK && state_hdr2 == HDR_OK) {
+ log_dbg(cd, "Primary LUKS2 header requires recovery.");
+
+ if (do_blkprobe && (r = detect_device_signatures(cd, device_path(device)))) {
+ log_err(cd, _("Device contains ambiguous signatures, cannot auto-recover LUKS2.\n"
+ "Please run \"cryptsetup repair\" for recovery."));
+ goto err;
+ }
+
+ if (do_recovery) {
+ memcpy(&hdr_disk1, &hdr_disk2, LUKS2_HDR_BIN_LEN);
+ r = crypt_random_get(cd, (char*)hdr_disk1.salt, sizeof(hdr_disk1.salt), CRYPT_RND_SALT);
+ if (r)
+ log_dbg(cd, "Cannot generate master salt.");
+ else {
+ hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
+ r = hdr_write_disk(cd, device, hdr, json_area2, 0);
+ }
+ if (r)
+ log_dbg(cd, "Primary LUKS2 header recovery failed.");
+ }
+ }
+
+ free(json_area1);
+ json_area1 = NULL;
+ free(json_area2);
+ json_area2 = NULL;
+
+ /* wrong lock for write mode during recovery attempt */
+ if (r == -EAGAIN)
+ goto err;
+
+ /*
+ * Even if status is failed, the second header includes salt.
+ */
+ if (state_hdr1 == HDR_OK) {
+ hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
+ hdr->jobj = jobj_hdr1;
+ json_object_put(jobj_hdr2);
+ } else if (state_hdr2 == HDR_OK) {
+ hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
+ hdr->jobj = jobj_hdr2;
+ json_object_put(jobj_hdr1);
+ }
+
+ /*
+ * FIXME: should this fail? At least one header was read correctly.
+ * r = (state_hdr1 == HDR_FAIL_IO || state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
+ */
+ return 0;
+err:
+ log_dbg(cd, "LUKS2 header read failed (%d).", r);
+
+ free(json_area1);
+ free(json_area2);
+ json_object_put(jobj_hdr1);
+ json_object_put(jobj_hdr2);
+ hdr->jobj = NULL;
+ return r;
+}
+
+int LUKS2_hdr_version_unlocked(struct crypt_device *cd, const char *backup_file)
+{
+ struct {
+ char magic[LUKS2_MAGIC_L];
+ uint16_t version;
+ } __attribute__ ((packed)) hdr;
+ struct device *device = NULL;
+ int r = 0, devfd = -1, flags;
+
+ if (!backup_file)
+ device = crypt_metadata_device(cd);
+ else if (device_alloc(cd, &device, backup_file) < 0)
+ return 0;
+
+ if (!device)
+ return 0;
+
+ flags = O_RDONLY;
+ if (device_direct_io(device))
+ flags |= O_DIRECT;
+
+ devfd = open(device_path(device), flags);
+ if (devfd < 0)
+ goto err;
+
+ if ((read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), &hdr, sizeof(hdr), 0) == sizeof(hdr)) &&
+ !memcmp(hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
+ r = (int)be16_to_cpu(hdr.version);
+err:
+ if (devfd != -1)
+ close(devfd);
+
+ if (backup_file)
+ device_free(cd, device);
+
+ return r;
+}
diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h
new file mode 100644
index 0000000..5fbb56e
--- /dev/null
+++ b/lib/luks2/luks2_internal.h
@@ -0,0 +1,182 @@
+/*
+ * LUKS - Linux Unified Key Setup v2
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef _CRYPTSETUP_LUKS2_INTERNAL_H
+#define _CRYPTSETUP_LUKS2_INTERNAL_H
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <json-c/json.h>
+
+#include "internal.h"
+#include "base64.h"
+#include "luks2.h"
+
+#define UNUSED(x) (void)(x)
+
+/* override useless forward slash escape when supported by json-c */
+#ifndef JSON_C_TO_STRING_NOSLASHESCAPE
+#define JSON_C_TO_STRING_NOSLASHESCAPE 0
+#endif
+
+/*
+ * On-disk access function prototypes
+ */
+int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
+ struct device *device, int do_recovery, int do_blkprobe);
+int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr,
+ struct device *device);
+
+/*
+ * JSON struct access helpers
+ */
+json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot);
+json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token);
+json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int digest);
+json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment);
+json_object *LUKS2_get_tokens_jobj(struct luks2_hdr *hdr);
+
+void hexprint_base64(struct crypt_device *cd, json_object *jobj,
+ const char *sep, const char *line_sep);
+
+json_object *parse_json_len(struct crypt_device *cd, const char *json_area,
+ uint64_t max_length, int *json_len);
+uint64_t json_object_get_uint64(json_object *jobj);
+uint32_t json_object_get_uint32(json_object *jobj);
+json_object *json_object_new_uint64(uint64_t value);
+int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val);
+void json_object_object_del_by_uint(json_object *jobj, unsigned key);
+
+void JSON_DBG(struct crypt_device *cd, json_object *jobj, const char *desc);
+
+/*
+ * LUKS2 JSON validation
+ */
+
+/* validation helper */
+json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name,
+ const char *section, const char *key, json_type type);
+
+int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t json_size);
+int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_jobj,
+ json_object *hdr_keyslot, const char *key);
+int LUKS2_check_json_size(struct crypt_device *cd, const struct luks2_hdr *hdr);
+int LUKS2_token_validate(struct crypt_device *cd, json_object *hdr_jobj,
+ json_object *jobj_token, const char *key);
+void LUKS2_token_dump(struct crypt_device *cd, int token);
+
+/*
+ * LUKS2 JSON repair for known glitches
+ */
+void LUKS2_hdr_repair(struct crypt_device *cd, json_object *jobj_hdr);
+void LUKS2_keyslots_repair(struct crypt_device *cd, json_object *jobj_hdr);
+
+/*
+ * JSON array helpers
+ */
+struct json_object *LUKS2_array_jobj(struct json_object *array, const char *num);
+struct json_object *LUKS2_array_remove(struct json_object *array, const char *num);
+
+/*
+ * Plugins API
+ */
+
+/**
+ * LUKS2 keyslots handlers (EXPERIMENTAL)
+ */
+typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot,
+ size_t volume_key_len,
+ const struct luks2_keyslot_params *params);
+typedef int (*keyslot_update_func)(struct crypt_device *cd, int keyslot,
+ const struct luks2_keyslot_params *params);
+typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot,
+ const char *password, size_t password_len,
+ char *volume_key, size_t volume_key_len);
+typedef int (*keyslot_store_func)(struct crypt_device *cd, int keyslot,
+ const char *password, size_t password_len,
+ const char *volume_key, size_t volume_key_len);
+typedef int (*keyslot_wipe_func) (struct crypt_device *cd, int keyslot);
+typedef int (*keyslot_dump_func) (struct crypt_device *cd, int keyslot);
+typedef int (*keyslot_validate_func) (struct crypt_device *cd, json_object *jobj_keyslot);
+typedef void(*keyslot_repair_func) (struct crypt_device *cd, json_object *jobj_keyslot);
+
+/* see LUKS2_luks2_to_luks1 */
+int placeholder_keyslot_alloc(struct crypt_device *cd,
+ int keyslot,
+ uint64_t area_offset,
+ uint64_t area_length,
+ size_t volume_key_len);
+
+/* validate all keyslot implementations in hdr json */
+int LUKS2_keyslots_validate(struct crypt_device *cd, json_object *hdr_jobj);
+
+typedef struct {
+ const char *name;
+ keyslot_alloc_func alloc;
+ keyslot_update_func update;
+ keyslot_open_func open;
+ keyslot_store_func store;
+ keyslot_wipe_func wipe;
+ keyslot_dump_func dump;
+ keyslot_validate_func validate;
+ keyslot_repair_func repair;
+} keyslot_handler;
+
+/**
+ * LUKS2 digest handlers (EXPERIMENTAL)
+ */
+typedef int (*digest_verify_func)(struct crypt_device *cd, int digest,
+ const char *volume_key, size_t volume_key_len);
+typedef int (*digest_store_func) (struct crypt_device *cd, int digest,
+ const char *volume_key, size_t volume_key_len);
+typedef int (*digest_dump_func) (struct crypt_device *cd, int digest);
+
+typedef struct {
+ const char *name;
+ digest_verify_func verify;
+ digest_store_func store;
+ digest_dump_func dump;
+} digest_handler;
+
+const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type);
+
+/**
+ * LUKS2 token handlers (internal use only)
+ */
+typedef int (*builtin_token_get_func) (json_object *jobj_token, void *params);
+typedef int (*builtin_token_set_func) (json_object **jobj_token, const void *params);
+
+typedef struct {
+ /* internal only section used by builtin tokens */
+ builtin_token_get_func get;
+ builtin_token_set_func set;
+ /* public token handler */
+ const crypt_token_handler *h;
+} token_handler;
+
+int token_keyring_set(json_object **, const void *);
+int token_keyring_get(json_object *, void *);
+
+int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
+ size_t keylength, uint64_t *area_offset, uint64_t *area_length);
+
+#endif
diff --git a/lib/luks2/luks2_json_format.c b/lib/luks2/luks2_json_format.c
new file mode 100644
index 0000000..a2cd331
--- /dev/null
+++ b/lib/luks2/luks2_json_format.c
@@ -0,0 +1,311 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, LUKS2 header format code
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+#include <uuid/uuid.h>
+#include <assert.h>
+
+struct area {
+ uint64_t offset;
+ uint64_t length;
+};
+
+static size_t get_area_size(size_t keylength)
+{
+ //FIXME: calculate this properly, for now it is AF_split_sectors
+ return size_round_up(keylength * 4000, 4096);
+}
+
+static size_t get_min_offset(struct luks2_hdr *hdr)
+{
+ return 2 * hdr->hdr_size;
+}
+
+static size_t get_max_offset(struct crypt_device *cd)
+{
+ return crypt_get_data_offset(cd) * SECTOR_SIZE;
+}
+
+int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
+ size_t keylength, uint64_t *area_offset, uint64_t *area_length)
+{
+ struct area areas[LUKS2_KEYSLOTS_MAX], sorted_areas[LUKS2_KEYSLOTS_MAX] = {};
+ int i, j, k, area_i;
+ size_t offset, length;
+
+ /* fill area offset + length table */
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
+ if (!LUKS2_keyslot_area(hdr, i, &areas[i].offset, &areas[i].length))
+ continue;
+ areas[i].length = 0;
+ areas[i].offset = 0;
+ }
+
+ /* sort table */
+ k = 0; /* index in sorted table */
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
+ offset = get_max_offset(cd) ?: UINT64_MAX;
+ area_i = -1;
+ /* search for the smallest offset in table */
+ for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++)
+ if (areas[j].offset && areas[j].offset <= offset) {
+ area_i = j;
+ offset = areas[j].offset;
+ }
+
+ if (area_i >= 0) {
+ sorted_areas[k].length = areas[area_i].length;
+ sorted_areas[k].offset = areas[area_i].offset;
+ areas[area_i].length = 0;
+ areas[area_i].offset = 0;
+ k++;
+ }
+ }
+
+ /* search for the gap we can use */
+ offset = get_min_offset(hdr);
+ length = get_area_size(keylength);
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
+ /* skip empty */
+ if (sorted_areas[i].offset == 0 || sorted_areas[i].length == 0)
+ continue;
+
+ /* enough space before the used area */
+ if ((offset < sorted_areas[i].offset) && ((offset + length) <= sorted_areas[i].offset))
+ break;
+
+ /* both offset and length are already aligned to 4096 bytes */
+ offset = sorted_areas[i].offset + sorted_areas[i].length;
+ }
+
+ if (get_max_offset(cd) && (offset + length) > get_max_offset(cd)) {
+ log_err(cd, _("No space for new keyslot."));
+ return -EINVAL;
+ }
+
+ log_dbg(cd, "Found area %zu -> %zu", offset, length + offset);
+/*
+ log_dbg("Area offset min: %zu, max %zu, slots max %u",
+ get_min_offset(hdr), get_max_offset(cd), LUKS2_KEYSLOTS_MAX);
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
+ log_dbg("SLOT[%02i]: %-8" PRIu64 " -> %-8" PRIu64, i,
+ sorted_areas[i].offset,
+ sorted_areas[i].length + sorted_areas[i].offset);
+*/
+ *area_offset = offset;
+ *area_length = length;
+ return 0;
+}
+
+int LUKS2_check_metadata_area_size(uint64_t metadata_size)
+{
+ /* see LUKS2_HDR2_OFFSETS */
+ return (metadata_size != 0x004000 &&
+ metadata_size != 0x008000 && metadata_size != 0x010000 &&
+ metadata_size != 0x020000 && metadata_size != 0x040000 &&
+ metadata_size != 0x080000 && metadata_size != 0x100000 &&
+ metadata_size != 0x200000 && metadata_size != 0x400000);
+}
+
+int LUKS2_check_keyslots_area_size(uint64_t keyslots_size)
+{
+ return (MISALIGNED_4K(keyslots_size) ||
+ keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE);
+}
+
+int LUKS2_generate_hdr(
+ struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk,
+ const char *cipherName,
+ const char *cipherMode,
+ const char *integrity,
+ const char *uuid,
+ unsigned int sector_size, /* in bytes */
+ uint64_t data_offset, /* in bytes */
+ uint64_t align_offset, /* in bytes */
+ uint64_t required_alignment,
+ uint64_t metadata_size,
+ uint64_t keyslots_size)
+{
+ struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
+ char cipher[128];
+ uuid_t partitionUuid;
+ int digest;
+
+ if (!metadata_size)
+ metadata_size = LUKS2_HDR_16K_LEN;
+ hdr->hdr_size = metadata_size;
+
+ if (data_offset && data_offset < get_min_offset(hdr)) {
+ log_err(cd, _("Requested data offset is too small."));
+ return -EINVAL;
+ }
+
+ /* Increase keyslot size according to data offset */
+ if (!keyslots_size && data_offset)
+ keyslots_size = data_offset - get_min_offset(hdr);
+
+ /* keyslots size has to be 4 KiB aligned */
+ keyslots_size -= (keyslots_size % 4096);
+
+ if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
+ keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
+
+ if (!keyslots_size) {
+ assert(LUKS2_DEFAULT_HDR_SIZE > 2 * LUKS2_HDR_OFFSET_MAX);
+ keyslots_size = LUKS2_DEFAULT_HDR_SIZE - get_min_offset(hdr);
+ }
+
+ /* Decrease keyslots_size if we have smaller data_offset */
+ if (data_offset && (keyslots_size + get_min_offset(hdr)) > data_offset) {
+ keyslots_size = data_offset - get_min_offset(hdr);
+ log_dbg(cd, "Decreasing keyslot area size to %" PRIu64
+ " bytes due to the requested data offset %"
+ PRIu64 " bytes.", keyslots_size, data_offset);
+ }
+
+ /* Data offset has priority */
+ if (!data_offset && required_alignment) {
+ data_offset = size_round_up(get_min_offset(hdr) + keyslots_size,
+ (size_t)required_alignment);
+ data_offset += align_offset;
+ }
+
+ log_dbg(cd, "Formatting LUKS2 with JSON metadata area %" PRIu64
+ " bytes and keyslots area %" PRIu64 " bytes.",
+ metadata_size - LUKS2_HDR_BIN_LEN, keyslots_size);
+
+ if (keyslots_size < (LUKS2_HDR_OFFSET_MAX - 2*LUKS2_HDR_16K_LEN))
+ log_std(cd, _("WARNING: keyslots area (%" PRIu64 " bytes) is very small,"
+ " available LUKS2 keyslot count is very limited.\n"),
+ keyslots_size);
+
+ hdr->seqid = 1;
+ hdr->version = 2;
+ memset(hdr->label, 0, LUKS2_LABEL_L);
+ strcpy(hdr->checksum_alg, "sha256");
+ crypt_random_get(cd, (char*)hdr->salt1, LUKS2_SALT_L, CRYPT_RND_SALT);
+ crypt_random_get(cd, (char*)hdr->salt2, LUKS2_SALT_L, CRYPT_RND_SALT);
+
+ if (uuid && uuid_parse(uuid, partitionUuid) == -1) {
+ log_err(cd, _("Wrong LUKS UUID format provided."));
+ return -EINVAL;
+ }
+ if (!uuid)
+ uuid_generate(partitionUuid);
+
+ uuid_unparse(partitionUuid, hdr->uuid);
+
+ if (*cipherMode != '\0')
+ snprintf(cipher, sizeof(cipher), "%s-%s", cipherName, cipherMode);
+ else
+ snprintf(cipher, sizeof(cipher), "%s", cipherName);
+
+ hdr->jobj = json_object_new_object();
+
+ jobj_keyslots = json_object_new_object();
+ json_object_object_add(hdr->jobj, "keyslots", jobj_keyslots);
+ json_object_object_add(hdr->jobj, "tokens", json_object_new_object());
+ jobj_segments = json_object_new_object();
+ json_object_object_add(hdr->jobj, "segments", jobj_segments);
+ json_object_object_add(hdr->jobj, "digests", json_object_new_object());
+ jobj_config = json_object_new_object();
+ json_object_object_add(hdr->jobj, "config", jobj_config);
+
+ digest = LUKS2_digest_create(cd, "pbkdf2", hdr, vk);
+ if (digest < 0) {
+ json_object_put(hdr->jobj);
+ hdr->jobj = NULL;
+ return -EINVAL;
+ }
+
+ if (LUKS2_digest_segment_assign(cd, hdr, CRYPT_DEFAULT_SEGMENT, digest, 1, 0) < 0) {
+ json_object_put(hdr->jobj);
+ hdr->jobj = NULL;
+ return -EINVAL;
+ }
+
+ jobj_segment = json_object_new_object();
+ json_object_object_add(jobj_segment, "type", json_object_new_string("crypt"));
+ json_object_object_add(jobj_segment, "offset", json_object_new_uint64(data_offset));
+ json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string("0"));
+ json_object_object_add(jobj_segment, "size", json_object_new_string("dynamic"));
+ json_object_object_add(jobj_segment, "encryption", json_object_new_string(cipher));
+ json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
+
+ if (integrity) {
+ jobj_integrity = json_object_new_object();
+ json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
+ json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
+ json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
+ json_object_object_add(jobj_segment, "integrity", jobj_integrity);
+ }
+
+ json_object_object_add_by_uint(jobj_segments, CRYPT_DEFAULT_SEGMENT, jobj_segment);
+
+ json_object_object_add(jobj_config, "json_size", json_object_new_uint64(metadata_size - LUKS2_HDR_BIN_LEN));
+ json_object_object_add(jobj_config, "keyslots_size", json_object_new_uint64(keyslots_size));
+
+ JSON_DBG(cd, hdr->jobj, "Header JSON:");
+ return 0;
+}
+
+int LUKS2_wipe_header_areas(struct crypt_device *cd,
+ struct luks2_hdr *hdr)
+{
+ int r;
+ uint64_t offset, length;
+ size_t wipe_block;
+
+ /* Wipe complete header, keyslots and padding areas with zeroes. */
+ offset = 0;
+ length = LUKS2_get_data_offset(hdr) * SECTOR_SIZE;
+ wipe_block = 1024 * 1024;
+
+ if (LUKS2_hdr_validate(cd, hdr->jobj, hdr->hdr_size - LUKS2_HDR_BIN_LEN))
+ return -EINVAL;
+
+ /* On detached header wipe at least the first 4k */
+ if (length == 0) {
+ length = 4096;
+ wipe_block = 4096;
+ }
+
+ log_dbg(cd, "Wiping LUKS areas (0x%06" PRIx64 " - 0x%06" PRIx64") with zeroes.",
+ offset, length + offset);
+
+ r = crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO,
+ offset, length, wipe_block, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ /* Wipe keyslot area */
+ wipe_block = 1024 * 1024;
+ offset = get_min_offset(hdr);
+ length = LUKS2_keyslots_size(hdr->jobj);
+
+ log_dbg(cd, "Wiping keyslots area (0x%06" PRIx64 " - 0x%06" PRIx64") with random data.",
+ offset, length + offset);
+
+ return crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_RANDOM,
+ offset, length, wipe_block, NULL, NULL);
+}
diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c
new file mode 100644
index 0000000..de8d018
--- /dev/null
+++ b/lib/luks2/luks2_json_metadata.c
@@ -0,0 +1,1935 @@
+/*
+ * LUKS - Linux Unified Key Setup v2
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ * Copyright (C) 2015-2019 Ondrej Kozina
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+#include "../integrity/integrity.h"
+#include <assert.h>
+#include <ctype.h>
+#include <uuid/uuid.h>
+
+#define LUKS_STRIPES 4000
+
+struct interval {
+ uint64_t offset;
+ uint64_t length;
+};
+
+void hexprint_base64(struct crypt_device *cd, json_object *jobj,
+ const char *sep, const char *line_sep)
+{
+ char *buf = NULL;
+ size_t buf_len;
+ unsigned int i;
+
+ if (!base64_decode_alloc(json_object_get_string(jobj),
+ json_object_get_string_len(jobj),
+ &buf, &buf_len))
+ return;
+
+ for (i = 0; i < buf_len / 2; i++)
+ log_std(cd, "%02hhx%s", buf[i], sep);
+ log_std(cd, "\n\t%s", line_sep);
+ for (i = buf_len / 2; i < buf_len; i++)
+ log_std(cd, "%02hhx%s", buf[i], sep);
+ log_std(cd, "\n");
+ free(buf);
+}
+
+void JSON_DBG(struct crypt_device *cd, json_object *jobj, const char *desc)
+{
+ if (desc)
+ crypt_log(cd, CRYPT_LOG_DEBUG_JSON, desc);
+ crypt_log(cd, CRYPT_LOG_DEBUG_JSON, json_object_to_json_string_ext(jobj,
+ JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE));
+}
+
+/*
+ * JSON array helpers
+ */
+struct json_object *LUKS2_array_jobj(struct json_object *array, const char *num)
+{
+ struct json_object *jobj1;
+ int i;
+
+ for (i = 0; i < (int) json_object_array_length(array); i++) {
+ jobj1 = json_object_array_get_idx(array, i);
+ if (!strcmp(num, json_object_get_string(jobj1)))
+ return jobj1;
+ }
+
+ return NULL;
+}
+
+struct json_object *LUKS2_array_remove(struct json_object *array, const char *num)
+{
+ struct json_object *jobj1, *jobj_removing = NULL, *array_new;
+ int i;
+
+ jobj_removing = LUKS2_array_jobj(array, num);
+ if (!jobj_removing)
+ return NULL;
+
+ /* Create new array without jobj_removing. */
+ array_new = json_object_new_array();
+ for (i = 0; i < (int) json_object_array_length(array); i++) {
+ jobj1 = json_object_array_get_idx(array, i);
+ if (jobj1 != jobj_removing)
+ json_object_array_add(array_new, json_object_get(jobj1));
+ }
+
+ return array_new;
+}
+
+/*
+ * JSON struct access helpers
+ */
+json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot)
+{
+ json_object *jobj1, *jobj2;
+ char keyslot_name[16];
+
+ if (!hdr || keyslot < 0)
+ return NULL;
+
+ if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
+ return NULL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj1))
+ return NULL;
+
+ json_object_object_get_ex(jobj1, keyslot_name, &jobj2);
+ return jobj2;
+}
+
+json_object *LUKS2_get_tokens_jobj(struct luks2_hdr *hdr)
+{
+ json_object *jobj_tokens;
+
+ if (!hdr || !json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens))
+ return NULL;
+
+ return jobj_tokens;
+}
+
+json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token)
+{
+ json_object *jobj1, *jobj2;
+ char token_name[16];
+
+ if (!hdr || token < 0)
+ return NULL;
+
+ jobj1 = LUKS2_get_tokens_jobj(hdr);
+ if (!jobj1)
+ return NULL;
+
+ if (snprintf(token_name, sizeof(token_name), "%u", token) < 1)
+ return NULL;
+
+ json_object_object_get_ex(jobj1, token_name, &jobj2);
+ return jobj2;
+}
+
+json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int digest)
+{
+ json_object *jobj1, *jobj2;
+ char digest_name[16];
+
+ if (!hdr || digest < 0)
+ return NULL;
+
+ if (snprintf(digest_name, sizeof(digest_name), "%u", digest) < 1)
+ return NULL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "digests", &jobj1))
+ return NULL;
+
+ json_object_object_get_ex(jobj1, digest_name, &jobj2);
+ return jobj2;
+}
+
+json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment)
+{
+ json_object *jobj1, *jobj2;
+ char segment_name[16];
+
+ if (!hdr || segment < 0)
+ return NULL;
+
+ if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
+ return NULL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj1))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, segment_name, &jobj2))
+ return NULL;
+
+ return jobj2;
+}
+
+/*
+ * json_type_int needs to be validated first.
+ * See validate_json_uint32()
+ */
+uint32_t json_object_get_uint32(json_object *jobj)
+{
+ return json_object_get_int64(jobj);
+}
+
+/* jobj has to be json_type_string and numbered */
+static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value)
+{
+ char *endptr;
+ unsigned long long tmp;
+
+ errno = 0;
+ tmp = strtoull(json_object_get_string(jobj), &endptr, 10);
+ if (*endptr || errno) {
+ *value = 0;
+ return FALSE;
+ }
+
+ *value = tmp;
+ return TRUE;
+}
+
+uint64_t json_object_get_uint64(json_object *jobj)
+{
+ uint64_t r;
+ return json_str_to_uint64(jobj, &r) ? r : 0;
+}
+
+json_object *json_object_new_uint64(uint64_t value)
+{
+ /* 18446744073709551615 */
+ char num[21];
+ int r;
+ json_object *jobj;
+
+ r = snprintf(num, sizeof(num), "%" PRIu64, value);
+ if (r < 0 || (size_t)r >= sizeof(num))
+ return NULL;
+
+ jobj = json_object_new_string(num);
+ return jobj;
+}
+
+/*
+ * Validate helpers
+ */
+static json_bool numbered(struct crypt_device *cd, const char *name, const char *key)
+{
+ int i;
+
+ for (i = 0; key[i]; i++)
+ if (!isdigit(key[i])) {
+ log_dbg(cd, "%s \"%s\" is not in numbered form.", name, key);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name,
+ const char *section, const char *key, json_type type)
+{
+ json_object *sobj;
+
+ if (!json_object_object_get_ex(jobj, key, &sobj) ||
+ !json_object_is_type(sobj, type)) {
+ log_dbg(cd, "%s \"%s\" is missing \"%s\" (%s) specification.",
+ section, name, key, json_type_to_name(type));
+ return NULL;
+ }
+
+ return sobj;
+}
+
+/* use only on already validated 'segments' object */
+static uint64_t get_first_data_offset(json_object *jobj_segs, const char *type)
+{
+ json_object *jobj_offset, *jobj_type;
+ uint64_t tmp, min = UINT64_MAX;
+
+ json_object_object_foreach(jobj_segs, key, val) {
+ UNUSED(key);
+
+ if (type) {
+ json_object_object_get_ex(val, "type", &jobj_type);
+ if (strcmp(type, json_object_get_string(jobj_type)))
+ continue;
+ }
+
+ json_object_object_get_ex(val, "offset", &jobj_offset);
+ tmp = json_object_get_uint64(jobj_offset);
+
+ if (!tmp)
+ return tmp;
+
+ if (tmp < min)
+ min = tmp;
+ }
+
+ return min;
+}
+
+static json_bool validate_json_uint32(json_object *jobj)
+{
+ int64_t tmp;
+
+ errno = 0;
+ tmp = json_object_get_int64(jobj);
+
+ return (errno || tmp < 0 || tmp > UINT32_MAX) ? FALSE : TRUE;
+}
+
+static json_bool validate_keyslots_array(struct crypt_device *cd,
+ json_object *jarr, json_object *jobj_keys)
+{
+ json_object *jobj;
+ int i = 0, length = (int) json_object_array_length(jarr);
+
+ while (i < length) {
+ jobj = json_object_array_get_idx(jarr, i);
+ if (!json_object_is_type(jobj, json_type_string)) {
+ log_dbg(cd, "Illegal value type in keyslots array at index %d.", i);
+ return FALSE;
+ }
+
+ if (!json_contains(cd, jobj_keys, "", "Keyslots section",
+ json_object_get_string(jobj), json_type_object))
+ return FALSE;
+
+ i++;
+ }
+
+ return TRUE;
+}
+
+static json_bool validate_segments_array(struct crypt_device *cd,
+ json_object *jarr, json_object *jobj_segments)
+{
+ json_object *jobj;
+ int i = 0, length = (int) json_object_array_length(jarr);
+
+ while (i < length) {
+ jobj = json_object_array_get_idx(jarr, i);
+ if (!json_object_is_type(jobj, json_type_string)) {
+ log_dbg(cd, "Illegal value type in segments array at index %d.", i);
+ return FALSE;
+ }
+
+ if (!json_contains(cd, jobj_segments, "", "Segments section",
+ json_object_get_string(jobj), json_type_object))
+ return FALSE;
+
+ i++;
+ }
+
+ return TRUE;
+}
+
+static json_bool segment_has_digest(const char *segment_name, json_object *jobj_digests)
+{
+ json_object *jobj_segments;
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ UNUSED(key);
+ json_object_object_get_ex(val, "segments", &jobj_segments);
+ if (LUKS2_array_jobj(jobj_segments, segment_name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static json_bool validate_intervals(struct crypt_device *cd,
+ int length, const struct interval *ix,
+ uint64_t metadata_size, uint64_t keyslots_area_end)
+{
+ int j, i = 0;
+
+ while (i < length) {
+ if (ix[i].offset < 2 * metadata_size) {
+ log_dbg(cd, "Illegal area offset: %" PRIu64 ".", ix[i].offset);
+ return FALSE;
+ }
+
+ if (!ix[i].length) {
+ log_dbg(cd, "Area length must be greater than zero.");
+ return FALSE;
+ }
+
+ if ((ix[i].offset + ix[i].length) > keyslots_area_end) {
+ log_dbg(cd, "Area [%" PRIu64 ", %" PRIu64 "] overflows binary keyslots area (ends at offset: %" PRIu64 ").",
+ ix[i].offset, ix[i].offset + ix[i].length, keyslots_area_end);
+ return FALSE;
+ }
+
+ for (j = 0; j < length; j++) {
+ if (i == j)
+ continue;
+ if ((ix[i].offset >= ix[j].offset) && (ix[i].offset < (ix[j].offset + ix[j].length))) {
+ log_dbg(cd, "Overlapping areas [%" PRIu64 ",%" PRIu64 "] and [%" PRIu64 ",%" PRIu64 "].",
+ ix[i].offset, ix[i].offset + ix[i].length,
+ ix[j].offset, ix[j].offset + ix[j].length);
+ return FALSE;
+ }
+ }
+
+ i++;
+ }
+
+ return TRUE;
+}
+
+int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_jobj, json_object *hdr_keyslot, const char *key)
+{
+ json_object *jobj_key_size;
+
+ if (!json_contains(cd, hdr_keyslot, key, "Keyslot", "type", json_type_string))
+ return 1;
+ if (!(jobj_key_size = json_contains(cd, hdr_keyslot, key, "Keyslot", "key_size", json_type_int)))
+ return 1;
+
+ /* enforce uint32_t type */
+ if (!validate_json_uint32(jobj_key_size)) {
+ log_dbg(cd, "Illegal field \"key_size\":%s.",
+ json_object_get_string(jobj_key_size));
+ return 1;
+ }
+
+ return 0;
+}
+
+int LUKS2_token_validate(struct crypt_device *cd,
+ json_object *hdr_jobj, json_object *jobj_token, const char *key)
+{
+ json_object *jarr, *jobj_keyslots;
+
+ /* keyslots are not yet validated, but we need to know token doesn't reference missing keyslot */
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+ return 1;
+
+ if (!json_contains(cd, jobj_token, key, "Token", "type", json_type_string))
+ return 1;
+
+ jarr = json_contains(cd, jobj_token, key, "Token", "keyslots", json_type_array);
+ if (!jarr)
+ return 1;
+
+ if (!validate_keyslots_array(cd, jarr, jobj_keyslots))
+ return 1;
+
+ return 0;
+}
+
+static int hdr_validate_json_size(struct crypt_device *cd, json_object *hdr_jobj, uint64_t hdr_json_size)
+{
+ json_object *jobj, *jobj1;
+ const char *json;
+ uint64_t json_area_size, json_size;
+
+ json_object_object_get_ex(hdr_jobj, "config", &jobj);
+ json_object_object_get_ex(jobj, "json_size", &jobj1);
+
+ json = json_object_to_json_string_ext(hdr_jobj,
+ JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
+ json_area_size = json_object_get_uint64(jobj1);
+ json_size = (uint64_t)strlen(json);
+
+ if (hdr_json_size != json_area_size) {
+ log_dbg(cd, "JSON area size doesn't match value in binary header.");
+ return 1;
+ }
+
+ if (json_size > json_area_size) {
+ log_dbg(cd, "JSON doesn't fit in the designated area.");
+ return 1;
+ }
+
+ return 0;
+}
+
+int LUKS2_check_json_size(struct crypt_device *cd, const struct luks2_hdr *hdr)
+{
+ return hdr_validate_json_size(cd, hdr->jobj, hdr->hdr_size - LUKS2_HDR_BIN_LEN);
+}
+
+static int hdr_validate_keyslots(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jobj;
+
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj)) {
+ log_dbg(cd, "Missing keyslots section.");
+ return 1;
+ }
+
+ json_object_object_foreach(jobj, key, val) {
+ if (!numbered(cd, "Keyslot", key))
+ return 1;
+ if (LUKS2_keyslot_validate(cd, hdr_jobj, val, key))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int hdr_validate_tokens(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jobj;
+
+ if (!json_object_object_get_ex(hdr_jobj, "tokens", &jobj)) {
+ log_dbg(cd, "Missing tokens section.");
+ return 1;
+ }
+
+ json_object_object_foreach(jobj, key, val) {
+ if (!numbered(cd, "Token", key))
+ return 1;
+ if (LUKS2_token_validate(cd, hdr_jobj, val, key))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int hdr_validate_crypt_segment(struct crypt_device *cd,
+ json_object *jobj, const char *key, json_object *jobj_digests,
+ uint64_t offset, uint64_t size)
+{
+ json_object *jobj_ivoffset, *jobj_sector_size, *jobj_integrity;
+ uint32_t sector_size;
+ uint64_t ivoffset;
+
+ if (!(jobj_ivoffset = json_contains(cd, jobj, key, "Segment", "iv_tweak", json_type_string)) ||
+ !json_contains(cd, jobj, key, "Segment", "encryption", json_type_string) ||
+ !(jobj_sector_size = json_contains(cd, jobj, key, "Segment", "sector_size", json_type_int)))
+ return 1;
+
+ /* integrity */
+ if (json_object_object_get_ex(jobj, "integrity", &jobj_integrity)) {
+ if (!json_contains(cd, jobj, key, "Segment", "integrity", json_type_object) ||
+ !json_contains(cd, jobj_integrity, key, "Segment integrity", "type", json_type_string) ||
+ !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_encryption", json_type_string) ||
+ !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_integrity", json_type_string))
+ return 1;
+ }
+
+ /* enforce uint32_t type */
+ if (!validate_json_uint32(jobj_sector_size)) {
+ log_dbg(cd, "Illegal field \"sector_size\":%s.",
+ json_object_get_string(jobj_sector_size));
+ return 1;
+ }
+
+ sector_size = json_object_get_uint32(jobj_sector_size);
+ if (!sector_size || MISALIGNED_512(sector_size)) {
+ log_dbg(cd, "Illegal sector size: %" PRIu32, sector_size);
+ return 1;
+ }
+
+ if (!numbered(cd, "iv_tweak", json_object_get_string(jobj_ivoffset)) ||
+ !json_str_to_uint64(jobj_ivoffset, &ivoffset)) {
+ log_dbg(cd, "Illegal iv_tweak value.");
+ return 1;
+ }
+
+ if (size % sector_size) {
+ log_dbg(cd, "Size field has to be aligned to sector size: %" PRIu32, sector_size);
+ return 1;
+ }
+
+ return !segment_has_digest(key, jobj_digests);
+}
+
+static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jobj, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags;
+ int i;
+ uint64_t offset, size;
+
+ if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj)) {
+ log_dbg(cd, "Missing segments section.");
+ return 1;
+ }
+
+ if (json_object_object_length(jobj) < 1) {
+ log_dbg(cd, "Empty segments section.");
+ return 1;
+ }
+
+ /* digests should already be validated */
+ if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj_digests))
+ return 1;
+
+ json_object_object_foreach(jobj, key, val) {
+ if (!numbered(cd, "Segment", key))
+ return 1;
+
+ /* those fields are mandatory for all segment types */
+ if (!(jobj_type = json_contains(cd, val, key, "Segment", "type", json_type_string)) ||
+ !(jobj_offset = json_contains(cd, val, key, "Segment", "offset", json_type_string)) ||
+ !(jobj_size = json_contains(cd, val, key, "Segment", "size", json_type_string)))
+ return 1;
+
+ if (!numbered(cd, "offset", json_object_get_string(jobj_offset)) ||
+ !json_str_to_uint64(jobj_offset, &offset))
+ return 1;
+
+ /* size "dynamic" means whole device starting at 'offset' */
+ if (strcmp(json_object_get_string(jobj_size), "dynamic")) {
+ if (!numbered(cd, "size", json_object_get_string(jobj_size)) ||
+ !json_str_to_uint64(jobj_size, &size) || !size)
+ return 1;
+ } else
+ size = 0;
+
+ /* all device-mapper devices are aligned to 512 sector size */
+ if (MISALIGNED_512(offset)) {
+ log_dbg(cd, "Offset field has to be aligned to sector size: %" PRIu32, SECTOR_SIZE);
+ return 1;
+ }
+ if (MISALIGNED_512(size)) {
+ log_dbg(cd, "Size field has to be aligned to sector size: %" PRIu32, SECTOR_SIZE);
+ return 1;
+ }
+
+ /* flags array is optional and must contain strings */
+ if (json_object_object_get_ex(val, "flags", NULL)) {
+ if (!(jobj_flags = json_contains(cd, val, key, "Segment", "flags", json_type_array)))
+ return 1;
+ for (i = 0; i < (int) json_object_array_length(jobj_flags); i++)
+ if (!json_object_is_type(json_object_array_get_idx(jobj_flags, i), json_type_string))
+ return 1;
+ }
+
+ /* crypt */
+ if (!strcmp(json_object_get_string(jobj_type), "crypt") &&
+ hdr_validate_crypt_segment(cd, val, key, jobj_digests, offset, size))
+ return 1;
+ }
+
+ return 0;
+}
+
+uint64_t LUKS2_metadata_size(json_object *jobj)
+{
+ json_object *jobj1, *jobj2;
+ uint64_t json_size;
+
+ json_object_object_get_ex(jobj, "config", &jobj1);
+ json_object_object_get_ex(jobj1, "json_size", &jobj2);
+ json_str_to_uint64(jobj2, &json_size);
+
+ return json_size + LUKS2_HDR_BIN_LEN;
+}
+
+static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ struct interval *intervals;
+ json_object *jobj_keyslots, *jobj_offset, *jobj_length, *jobj_segments, *jobj_area;
+ int length, ret, i = 0;
+ uint64_t metadata_size;
+
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+ return 1;
+
+ /* segments are already validated */
+ if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments))
+ return 1;
+
+ /* config is already validated */
+ metadata_size = LUKS2_metadata_size(hdr_jobj);
+
+ length = json_object_object_length(jobj_keyslots);
+
+ /* Empty section */
+ if (length == 0)
+ return 0;
+
+ if (length < 0) {
+ log_dbg(cd, "Invalid keyslot areas specification.");
+ return 1;
+ }
+
+ intervals = malloc(length * sizeof(*intervals));
+ if (!intervals) {
+ log_dbg(cd, "Not enough memory.");
+ return -ENOMEM;
+ }
+
+ json_object_object_foreach(jobj_keyslots, key, val) {
+
+ if (!(jobj_area = json_contains(cd, val, key, "Keyslot", "area", json_type_object)) ||
+ !(jobj_offset = json_contains(cd, jobj_area, key, "Keyslot", "offset", json_type_string)) ||
+ !(jobj_length = json_contains(cd, jobj_area, key, "Keyslot", "size", json_type_string)) ||
+ !numbered(cd, "offset", json_object_get_string(jobj_offset)) ||
+ !numbered(cd, "size", json_object_get_string(jobj_length))) {
+ free(intervals);
+ return 1;
+ }
+
+ /* rule out values > UINT64_MAX */
+ if (!json_str_to_uint64(jobj_offset, &intervals[i].offset) ||
+ !json_str_to_uint64(jobj_length, &intervals[i].length)) {
+ free(intervals);
+ return 1;
+ }
+
+ i++;
+ }
+
+ if (length != i) {
+ free(intervals);
+ return 1;
+ }
+
+ ret = validate_intervals(cd, length, intervals, metadata_size, LUKS2_hdr_and_areas_size(hdr_jobj)) ? 0 : 1;
+
+ free(intervals);
+
+ return ret;
+}
+
+static int hdr_validate_digests(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jarr_keys, *jarr_segs, *jobj, *jobj_keyslots, *jobj_segments;
+
+ if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj)) {
+ log_dbg(cd, "Missing digests section.");
+ return 1;
+ }
+
+ /* keyslots are not yet validated, but we need to know digest doesn't reference missing keyslot */
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+ return 1;
+
+ /* segments are not yet validated, but we need to know digest doesn't reference missing segment */
+ if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments))
+ return 1;
+
+ json_object_object_foreach(jobj, key, val) {
+ if (!numbered(cd, "Digest", key))
+ return 1;
+
+ if (!json_contains(cd, val, key, "Digest", "type", json_type_string) ||
+ !(jarr_keys = json_contains(cd, val, key, "Digest", "keyslots", json_type_array)) ||
+ !(jarr_segs = json_contains(cd, val, key, "Digest", "segments", json_type_array)))
+ return 1;
+
+ if (!validate_keyslots_array(cd, jarr_keys, jobj_keyslots))
+ return 1;
+ if (!validate_segments_array(cd, jarr_segs, jobj_segments))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jobj_config, *jobj, *jobj1;
+ int i;
+ uint64_t keyslots_size, metadata_size, segment_offset;
+
+ if (!json_object_object_get_ex(hdr_jobj, "config", &jobj_config)) {
+ log_dbg(cd, "Missing config section.");
+ return 1;
+ }
+
+ if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "json_size", json_type_string)) ||
+ !json_str_to_uint64(jobj, &metadata_size))
+ return 1;
+
+ /* single metadata instance is assembled from json area size plus
+ * binary header size */
+ metadata_size += LUKS2_HDR_BIN_LEN;
+
+ if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "keyslots_size", json_type_string)) ||
+ !json_str_to_uint64(jobj, &keyslots_size))
+ return 1;
+
+ if (LUKS2_check_metadata_area_size(metadata_size)) {
+ log_dbg(cd, "Unsupported LUKS2 header size (%" PRIu64 ").", metadata_size);
+ return 1;
+ }
+
+ if (LUKS2_check_keyslots_area_size(keyslots_size)) {
+ log_dbg(cd, "Unsupported LUKS2 keyslots size (%" PRIu64 ").", keyslots_size);
+ return 1;
+ }
+
+ /*
+ * validate keyslots_size fits in between (2 * metadata_size) and first
+ * segment_offset (except detached header)
+ */
+ json_object_object_get_ex(hdr_jobj, "segments", &jobj);
+ segment_offset = get_first_data_offset(jobj, "crypt");
+ if (segment_offset &&
+ (segment_offset < keyslots_size ||
+ (segment_offset - keyslots_size) < (2 * metadata_size))) {
+ log_dbg(cd, "keyslots_size is too large %" PRIu64 " (bytes). Data offset: %" PRIu64
+ ", keyslots offset: %" PRIu64, keyslots_size, segment_offset, 2 * metadata_size);
+ return 1;
+ }
+
+ /* Flags array is optional */
+ if (json_object_object_get_ex(jobj_config, "flags", &jobj)) {
+ if (!json_contains(cd, jobj_config, "section", "Config", "flags", json_type_array))
+ return 1;
+
+ /* All array members must be strings */
+ for (i = 0; i < (int) json_object_array_length(jobj); i++)
+ if (!json_object_is_type(json_object_array_get_idx(jobj, i), json_type_string))
+ return 1;
+ }
+
+ /* Requirements object is optional */
+ if (json_object_object_get_ex(jobj_config, "requirements", &jobj)) {
+ if (!json_contains(cd, jobj_config, "section", "Config", "requirements", json_type_object))
+ return 1;
+
+ /* Mandatory array is optional */
+ if (json_object_object_get_ex(jobj, "mandatory", &jobj1)) {
+ if (!json_contains(cd, jobj, "section", "Requirements", "mandatory", json_type_array))
+ return 1;
+
+ /* All array members must be strings */
+ for (i = 0; i < (int) json_object_array_length(jobj1); i++)
+ if (!json_object_is_type(json_object_array_get_idx(jobj1, i), json_type_string))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t json_size)
+{
+ struct {
+ int (*validate)(struct crypt_device *, json_object *);
+ } checks[] = {
+ { hdr_validate_tokens },
+ { hdr_validate_digests },
+ { hdr_validate_segments },
+ { hdr_validate_keyslots },
+ { hdr_validate_config },
+ { hdr_validate_areas },
+ { NULL }
+ };
+ int i;
+
+ if (!hdr_jobj)
+ return 1;
+
+ for (i = 0; checks[i].validate; i++)
+ if (checks[i].validate && checks[i].validate(cd, hdr_jobj))
+ return 1;
+
+ if (hdr_validate_json_size(cd, hdr_jobj, json_size))
+ return 1;
+
+ /* validate keyslot implementations */
+ if (LUKS2_keyslots_validate(cd, hdr_jobj))
+ return 1;
+
+ return 0;
+}
+
+/* FIXME: should we expose do_recovery parameter explicitly? */
+int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair)
+{
+ int r;
+
+ r = device_read_lock(cd, crypt_metadata_device(cd));
+ if (r) {
+ log_err(cd, _("Failed to acquire read lock on device %s."),
+ device_path(crypt_metadata_device(cd)));
+ return r;
+ }
+
+ r = LUKS2_disk_hdr_read(cd, hdr, crypt_metadata_device(cd), 1, !repair);
+ if (r == -EAGAIN) {
+ /* unlikely: auto-recovery is required and failed due to read lock being held */
+ device_read_unlock(cd, crypt_metadata_device(cd));
+
+ r = device_write_lock(cd, crypt_metadata_device(cd));
+ if (r) {
+ log_err(cd, _("Failed to acquire write lock on device %s."),
+ device_path(crypt_metadata_device(cd)));
+ return r;
+ }
+
+ r = LUKS2_disk_hdr_read(cd, hdr, crypt_metadata_device(cd), 1, !repair);
+
+ device_write_unlock(cd, crypt_metadata_device(cd));
+ } else
+ device_read_unlock(cd, crypt_metadata_device(cd));
+
+ return r;
+}
+
+int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+ /* NOTE: is called before LUKS2 validation routines */
+ /* erase unused digests (no assigned keyslot or segment) */
+ LUKS2_digests_erase_unused(cd, hdr);
+
+ if (LUKS2_hdr_validate(cd, hdr->jobj, hdr->hdr_size - LUKS2_HDR_BIN_LEN))
+ return -EINVAL;
+
+ return LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd));
+}
+
+int LUKS2_hdr_uuid(struct crypt_device *cd, struct luks2_hdr *hdr, const char *uuid)
+{
+ uuid_t partitionUuid;
+
+ if (uuid && uuid_parse(uuid, partitionUuid) == -1) {
+ log_err(cd, _("Wrong LUKS UUID format provided."));
+ return -EINVAL;
+ }
+ if (!uuid)
+ uuid_generate(partitionUuid);
+
+ uuid_unparse(partitionUuid, hdr->uuid);
+
+ return LUKS2_hdr_write(cd, hdr);
+}
+
+int LUKS2_hdr_labels(struct crypt_device *cd, struct luks2_hdr *hdr,
+ const char *label, const char *subsystem, int commit)
+{
+ //FIXME: check if the labels are the same and skip this.
+
+ memset(hdr->label, 0, LUKS2_LABEL_L);
+ if (label)
+ strncpy(hdr->label, label, LUKS2_LABEL_L-1);
+
+ memset(hdr->subsystem, 0, LUKS2_LABEL_L);
+ if (subsystem)
+ strncpy(hdr->subsystem, subsystem, LUKS2_LABEL_L-1);
+
+ return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+}
+
+void LUKS2_hdr_free(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+ if (json_object_put(hdr->jobj))
+ hdr->jobj = NULL;
+ else if (hdr->jobj)
+ log_dbg(cd, "LUKS2 header still in use");
+}
+
+uint64_t LUKS2_keyslots_size(json_object *jobj)
+{
+ json_object *jobj1, *jobj2;
+ uint64_t keyslots_size;
+
+ json_object_object_get_ex(jobj, "config", &jobj1);
+ json_object_object_get_ex(jobj1, "keyslots_size", &jobj2);
+ json_str_to_uint64(jobj2, &keyslots_size);
+
+ return keyslots_size;
+}
+
+uint64_t LUKS2_hdr_and_areas_size(json_object *jobj)
+{
+ return 2 * LUKS2_metadata_size(jobj) + LUKS2_keyslots_size(jobj);
+}
+
+int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
+ const char *backup_file)
+{
+ struct device *device = crypt_metadata_device(cd);
+ int r = 0, devfd = -1;
+ ssize_t hdr_size;
+ ssize_t buffer_size;
+ char *buffer = NULL;
+
+ hdr_size = LUKS2_hdr_and_areas_size(hdr->jobj);
+ buffer_size = size_round_up(hdr_size, crypt_getpagesize());
+
+ buffer = crypt_safe_alloc(buffer_size);
+ if (!buffer)
+ return -ENOMEM;
+
+ log_dbg(cd, "Storing backup of header (%zu bytes).", hdr_size);
+ log_dbg(cd, "Output backup file size: %zu bytes.", buffer_size);
+
+ r = device_read_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire read lock on device %s."),
+ device_path(crypt_metadata_device(cd)));
+ crypt_safe_free(buffer);
+ return r;
+ }
+
+ devfd = device_open_locked(cd, device, O_RDONLY);
+ if (devfd < 0) {
+ device_read_unlock(cd, device);
+ log_err(cd, _("Device %s is not a valid LUKS device."), device_path(device));
+ crypt_safe_free(buffer);
+ return devfd == -1 ? -EINVAL : devfd;
+ }
+
+ if (read_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), buffer, hdr_size) < hdr_size) {
+ close(devfd);
+ device_read_unlock(cd, device);
+ crypt_safe_free(buffer);
+ return -EIO;
+ }
+ close(devfd);
+
+ device_read_unlock(cd, device);
+
+ devfd = open(backup_file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR);
+ if (devfd == -1) {
+ if (errno == EEXIST)
+ log_err(cd, _("Requested header backup file %s already exists."), backup_file);
+ else
+ log_err(cd, _("Cannot create header backup file %s."), backup_file);
+ crypt_safe_free(buffer);
+ return -EINVAL;
+ }
+ if (write_buffer(devfd, buffer, buffer_size) < buffer_size) {
+ log_err(cd, _("Cannot write header backup file %s."), backup_file);
+ r = -EIO;
+ } else
+ r = 0;
+
+ close(devfd);
+ crypt_safe_free(buffer);
+ return r;
+}
+
+static int reqs_unknown(uint32_t reqs)
+{
+ return reqs & CRYPT_REQUIREMENT_UNKNOWN;
+}
+
+static int reqs_reencrypt(uint32_t reqs)
+{
+ return reqs & CRYPT_REQUIREMENT_OFFLINE_REENCRYPT;
+}
+
+int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
+ const char *backup_file)
+{
+ struct device *backup_device, *device = crypt_metadata_device(cd);
+ int r, devfd = -1, diff_uuid = 0;
+ ssize_t buffer_size = 0;
+ char *buffer = NULL, msg[1024];
+ struct luks2_hdr hdr_file;
+ struct luks2_hdr tmp_hdr = {};
+ uint32_t reqs = 0;
+
+ r = device_alloc(cd, &backup_device, backup_file);
+ if (r < 0)
+ return r;
+
+ /* FIXME: why lock backup device ? */
+ r = device_read_lock(cd, backup_device);
+ if (r) {
+ log_err(cd, _("Failed to acquire read lock on device %s."),
+ device_path(backup_device));
+ device_free(cd, backup_device);
+ return r;
+ }
+
+ r = LUKS2_disk_hdr_read(cd, &hdr_file, backup_device, 0, 0);
+ device_read_unlock(cd, backup_device);
+ device_free(cd, backup_device);
+
+ if (r < 0) {
+ log_err(cd, _("Backup file doesn't contain valid LUKS header."));
+ goto out;
+ }
+
+ /* do not allow header restore from backup with unmet requirements */
+ if (LUKS2_unmet_requirements(cd, &hdr_file, 0, 1)) {
+ log_err(cd, _("Forbidden LUKS2 requirements detected in backup %s."),
+ backup_file);
+ r = -ETXTBSY;
+ goto out;
+ }
+
+ buffer_size = LUKS2_hdr_and_areas_size(hdr_file.jobj);
+ buffer = crypt_safe_alloc(buffer_size);
+ if (!buffer) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ devfd = open(backup_file, O_RDONLY);
+ if (devfd == -1) {
+ log_err(cd, _("Cannot open header backup file %s."), backup_file);
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (read_buffer(devfd, buffer, buffer_size) < buffer_size) {
+ log_err(cd, _("Cannot read header backup file %s."), backup_file);
+ r = -EIO;
+ goto out;
+ }
+ close(devfd);
+ devfd = -1;
+
+ r = LUKS2_hdr_read(cd, &tmp_hdr, 0);
+ if (r == 0) {
+ log_dbg(cd, "Device %s already contains LUKS2 header, checking UUID and requirements.", device_path(device));
+ r = LUKS2_config_get_requirements(cd, &tmp_hdr, &reqs);
+ if (r)
+ goto out;
+
+ if (memcmp(tmp_hdr.uuid, hdr_file.uuid, LUKS2_UUID_L))
+ diff_uuid = 1;
+
+ if (!reqs_reencrypt(reqs)) {
+ log_dbg(cd, "Checking LUKS2 header size and offsets.");
+ if (LUKS2_get_data_offset(&tmp_hdr) != LUKS2_get_data_offset(&hdr_file)) {
+ log_err(cd, _("Data offset differ on device and backup, restore failed."));
+ r = -EINVAL;
+ goto out;
+ }
+ /* FIXME: what could go wrong? Erase if we're fine with consequences */
+ if (buffer_size != (ssize_t) LUKS2_hdr_and_areas_size(tmp_hdr.jobj)) {
+ log_err(cd, _("Binary header with keyslot areas size differ on device and backup, restore failed."));
+ r = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ r = snprintf(msg, sizeof(msg), _("Device %s %s%s%s%s"), device_path(device),
+ r ? _("does not contain LUKS2 header. Replacing header can destroy data on that device.") :
+ _("already contains LUKS2 header. Replacing header will destroy existing keyslots."),
+ diff_uuid ? _("\nWARNING: real device header has different UUID than backup!") : "",
+ reqs_unknown(reqs) ? _("\nWARNING: unknown LUKS2 requirements detected in real device header!"
+ "\nReplacing header with backup may corrupt the data on that device!") : "",
+ reqs_reencrypt(reqs) ? _("\nWARNING: Unfinished offline reencryption detected on the device!"
+ "\nReplacing header with backup may corrupt data.") : "");
+ if (r < 0 || (size_t) r >= sizeof(msg)) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ if (!crypt_confirm(cd, msg)) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ log_dbg(cd, "Storing backup of header (%zu bytes) to device %s.", buffer_size, device_path(device));
+
+ /* TODO: perform header restore on bdev in stand-alone routine? */
+ r = device_write_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire write lock on device %s."),
+ device_path(device));
+ goto out;
+ }
+
+ devfd = device_open_locked(cd, device, O_RDWR);
+ if (devfd < 0) {
+ if (errno == EACCES)
+ log_err(cd, _("Cannot write to device %s, permission denied."),
+ device_path(device));
+ else
+ log_err(cd, _("Cannot open device %s."), device_path(device));
+ device_write_unlock(cd, device);
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (write_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), buffer, buffer_size) < buffer_size)
+ r = -EIO;
+ else
+ r = 0;
+
+ device_write_unlock(cd, device);
+ /* end of TODO */
+
+out:
+ LUKS2_hdr_free(cd, hdr);
+ LUKS2_hdr_free(cd, &hdr_file);
+ LUKS2_hdr_free(cd, &tmp_hdr);
+ crypt_memzero(&hdr_file, sizeof(hdr_file));
+ crypt_memzero(&tmp_hdr, sizeof(tmp_hdr));
+ crypt_safe_free(buffer);
+
+ if (devfd >= 0) {
+ device_sync(cd, device, devfd);
+ close(devfd);
+ }
+
+
+ return r;
+}
+
+/*
+ * Persistent config flags
+ */
+static const struct {
+ uint32_t flag;
+ const char *description;
+} persistent_flags[] = {
+ { CRYPT_ACTIVATE_ALLOW_DISCARDS, "allow-discards" },
+ { CRYPT_ACTIVATE_SAME_CPU_CRYPT, "same-cpu-crypt" },
+ { CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS, "submit-from-crypt-cpus" },
+ { CRYPT_ACTIVATE_NO_JOURNAL, "no-journal" },
+ { 0, NULL }
+};
+
+int LUKS2_config_get_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *flags)
+{
+ json_object *jobj1, *jobj_config, *jobj_flags;
+ int i, j, found;
+
+ if (!hdr || !flags)
+ return -EINVAL;
+
+ *flags = 0;
+
+ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj_config, "flags", &jobj_flags))
+ return 0;
+
+ for (i = 0; i < (int) json_object_array_length(jobj_flags); i++) {
+ jobj1 = json_object_array_get_idx(jobj_flags, i);
+ found = 0;
+ for (j = 0; persistent_flags[j].description && !found; j++)
+ if (!strcmp(persistent_flags[j].description,
+ json_object_get_string(jobj1))) {
+ *flags |= persistent_flags[j].flag;
+ log_dbg(cd, "Using persistent flag %s.",
+ json_object_get_string(jobj1));
+ found = 1;
+ }
+ if (!found)
+ log_verbose(cd, _("Ignored unknown flag %s."),
+ json_object_get_string(jobj1));
+ }
+
+ return 0;
+}
+
+int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t flags)
+{
+ json_object *jobj_config, *jobj_flags;
+ int i;
+
+ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+ return 0;
+
+ jobj_flags = json_object_new_array();
+
+ for (i = 0; persistent_flags[i].description; i++) {
+ if (flags & persistent_flags[i].flag) {
+ log_dbg(cd, "Setting persistent flag: %s.", persistent_flags[i].description);
+ json_object_array_add(jobj_flags,
+ json_object_new_string(persistent_flags[i].description));
+ }
+ }
+
+ /* Replace or add new flags array */
+ json_object_object_add(jobj_config, "flags", jobj_flags);
+
+ return LUKS2_hdr_write(cd, hdr);
+}
+
+/*
+ * json format example (mandatory array must not be ignored,
+ * all other future fields may be added later)
+ *
+ * "requirements": {
+ * mandatory : [],
+ * optional0 : [],
+ * optional1 : "lala"
+ * }
+ */
+
+/* LUKS2 library requirements */
+static const struct {
+ uint32_t flag;
+ const char *description;
+} requirements_flags[] = {
+ { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT, "offline-reencrypt" },
+ { 0, NULL }
+};
+
+static uint32_t get_requirement_by_name(const char *requirement)
+{
+ int i;
+
+ for (i = 0; requirements_flags[i].description; i++)
+ if (!strcmp(requirement, requirements_flags[i].description))
+ return requirements_flags[i].flag;
+
+ return CRYPT_REQUIREMENT_UNKNOWN;
+}
+
+/*
+ * returns count of requirements (past cryptsetup 2.0 release)
+ */
+int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs)
+{
+ json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj;
+ int i, len;
+ uint32_t req;
+
+ assert(hdr);
+ if (!hdr || !reqs)
+ return -EINVAL;
+
+ *reqs = 0;
+
+ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory))
+ return 0;
+
+ len = (int) json_object_array_length(jobj_mandatory);
+ if (len <= 0)
+ return 0;
+
+ log_dbg(cd, "LUKS2 requirements detected:");
+
+ for (i = 0; i < len; i++) {
+ jobj = json_object_array_get_idx(jobj_mandatory, i);
+ req = get_requirement_by_name(json_object_get_string(jobj));
+ log_dbg(cd, "%s - %sknown", json_object_get_string(jobj),
+ reqs_unknown(req) ? "un" : "");
+ *reqs |= req;
+ }
+
+ return 0;
+}
+
+int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs)
+{
+ json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj;
+ int i, r = -EINVAL;
+
+ if (!hdr)
+ return -EINVAL;
+
+ jobj_mandatory = json_object_new_array();
+ if (!jobj_mandatory)
+ return -ENOMEM;
+
+ for (i = 0; requirements_flags[i].description; i++) {
+ if (reqs & requirements_flags[i].flag) {
+ jobj = json_object_new_string(requirements_flags[i].description);
+ if (!jobj) {
+ r = -ENOMEM;
+ goto err;
+ }
+ json_object_array_add(jobj_mandatory, jobj);
+ /* erase processed flag from input set */
+ reqs &= ~(requirements_flags[i].flag);
+ }
+ }
+
+ /* any remaining bit in requirements is unknown therefore illegal */
+ if (reqs) {
+ log_dbg(cd, "Illegal requirement flag(s) requested");
+ goto err;
+ }
+
+ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+ goto err;
+
+ if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) {
+ jobj_requirements = json_object_new_object();
+ if (!jobj_requirements) {
+ r = -ENOMEM;
+ goto err;
+ }
+ json_object_object_add(jobj_config, "requirements", jobj_requirements);
+ }
+
+ if (json_object_array_length(jobj_mandatory) > 0) {
+ /* replace mandatory field with new values */
+ json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory);
+ } else {
+ /* new mandatory field was empty, delete old one */
+ json_object_object_del(jobj_requirements, "mandatory");
+ json_object_put(jobj_mandatory);
+ }
+
+ /* remove empty requirements object */
+ if (!json_object_object_length(jobj_requirements))
+ json_object_object_del(jobj_config, "requirements");
+
+ return LUKS2_hdr_write(cd, hdr);
+err:
+ json_object_put(jobj_mandatory);
+ return r;
+}
+
+/*
+ * Header dump
+ */
+static void hdr_dump_config(struct crypt_device *cd, json_object *hdr_jobj)
+{
+
+ json_object *jobj1, *jobj_config, *jobj_flags, *jobj_requirements, *jobj_mandatory;
+ int i = 0, flags = 0, reqs = 0;
+
+ log_std(cd, "Flags: \t");
+
+ if (json_object_object_get_ex(hdr_jobj, "config", &jobj_config)) {
+ if (json_object_object_get_ex(jobj_config, "flags", &jobj_flags))
+ flags = (int) json_object_array_length(jobj_flags);
+ if (json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements) &&
+ json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory))
+ reqs = (int) json_object_array_length(jobj_mandatory);
+ }
+
+ for (i = 0; i < flags; i++) {
+ jobj1 = json_object_array_get_idx(jobj_flags, i);
+ log_std(cd, "%s ", json_object_get_string(jobj1));
+ }
+
+ log_std(cd, "%s\n%s", flags > 0 ? "" : "(no flags)", reqs > 0 ? "" : "\n");
+
+ if (reqs > 0) {
+ log_std(cd, "Requirements:\t");
+ for (i = 0; i < reqs; i++) {
+ jobj1 = json_object_array_get_idx(jobj_mandatory, i);
+ log_std(cd, "%s ", json_object_get_string(jobj1));
+ }
+ log_std(cd, "\n\n");
+ }
+}
+
+static const char *get_priority_desc(json_object *jobj)
+{
+ crypt_keyslot_priority priority;
+ json_object *jobj_priority;
+ const char *text;
+
+ if (json_object_object_get_ex(jobj, "priority", &jobj_priority))
+ priority = (crypt_keyslot_priority)(int)json_object_get_int(jobj_priority);
+ else
+ priority = CRYPT_SLOT_PRIORITY_NORMAL;
+
+ switch (priority) {
+ case CRYPT_SLOT_PRIORITY_IGNORE: text = "ignored"; break;
+ case CRYPT_SLOT_PRIORITY_PREFER: text = "preferred"; break;
+ case CRYPT_SLOT_PRIORITY_NORMAL: text = "normal"; break;
+ default: text = "invalid";
+ }
+
+ return text;
+}
+
+static void hdr_dump_keyslots(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ char slot[16];
+ json_object *keyslots_jobj, *digests_jobj, *jobj2, *jobj3, *val;
+ const char *tmps;
+ int i, j, r;
+
+ log_std(cd, "Keyslots:\n");
+ json_object_object_get_ex(hdr_jobj, "keyslots", &keyslots_jobj);
+
+ for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++) {
+ (void) snprintf(slot, sizeof(slot), "%i", j);
+ json_object_object_get_ex(keyslots_jobj, slot, &val);
+ if (!val)
+ continue;
+
+ json_object_object_get_ex(val, "type", &jobj2);
+ tmps = json_object_get_string(jobj2);
+
+ r = LUKS2_keyslot_for_segment(crypt_get_hdr(cd, CRYPT_LUKS2), j, CRYPT_DEFAULT_SEGMENT);
+ log_std(cd, " %s: %s%s\n", slot, tmps, r == -ENOENT ? " (unbound)" : "");
+
+ if (json_object_object_get_ex(val, "key_size", &jobj2))
+ log_std(cd, "\tKey: %u bits\n", json_object_get_uint32(jobj2) * 8);
+
+ log_std(cd, "\tPriority: %s\n", get_priority_desc(val));
+
+ LUKS2_keyslot_dump(cd, j);
+
+ json_object_object_get_ex(hdr_jobj, "digests", &digests_jobj);
+ json_object_object_foreach(digests_jobj, key2, val2) {
+ json_object_object_get_ex(val2, "keyslots", &jobj2);
+ for (i = 0; i < (int) json_object_array_length(jobj2); i++) {
+ jobj3 = json_object_array_get_idx(jobj2, i);
+ if (!strcmp(slot, json_object_get_string(jobj3))) {
+ log_std(cd, "\tDigest ID: %s\n", key2);
+ }
+ }
+ }
+ }
+}
+
+static void hdr_dump_tokens(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ char token[16];
+ json_object *tokens_jobj, *jobj2, *jobj3, *val;
+ const char *tmps;
+ int i, j;
+
+ log_std(cd, "Tokens:\n");
+ json_object_object_get_ex(hdr_jobj, "tokens", &tokens_jobj);
+
+ for (j = 0; j < LUKS2_TOKENS_MAX; j++) {
+ (void) snprintf(token, sizeof(token), "%i", j);
+ json_object_object_get_ex(tokens_jobj, token, &val);
+ if (!val)
+ continue;
+
+ json_object_object_get_ex(val, "type", &jobj2);
+ tmps = json_object_get_string(jobj2);
+ log_std(cd, " %s: %s\n", token, tmps);
+
+ LUKS2_token_dump(cd, j);
+
+ json_object_object_get_ex(val, "keyslots", &jobj2);
+ for (i = 0; i < (int) json_object_array_length(jobj2); i++) {
+ jobj3 = json_object_array_get_idx(jobj2, i);
+ log_std(cd, "\tKeyslot: %s\n", json_object_get_string(jobj3));
+ }
+ }
+}
+
+static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ char segment[16];
+ json_object *jobj_segments, *jobj_segment, *jobj1, *jobj2;
+ int i, j, flags;
+ uint64_t value;
+
+ log_std(cd, "Data segments:\n");
+ json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments);
+
+ for (i = 0; i < LUKS2_SEGMENT_MAX; i++) {
+ (void) snprintf(segment, sizeof(segment), "%i", i);
+ if (!json_object_object_get_ex(jobj_segments, segment, &jobj_segment))
+ continue;
+
+ json_object_object_get_ex(jobj_segment, "type", &jobj1);
+ log_std(cd, " %s: %s\n", segment, json_object_get_string(jobj1));
+
+ json_object_object_get_ex(jobj_segment, "offset", &jobj1);
+ json_str_to_uint64(jobj1, &value);
+ log_std(cd, "\toffset: %" PRIu64 " [bytes]\n", value);
+
+ json_object_object_get_ex(jobj_segment, "size", &jobj1);
+ if (!(strcmp(json_object_get_string(jobj1), "dynamic")))
+ log_std(cd, "\tlength: (whole device)\n");
+ else {
+ json_str_to_uint64(jobj1, &value);
+ log_std(cd, "\tlength: %" PRIu64 " [bytes]\n", value);
+ }
+
+ if (json_object_object_get_ex(jobj_segment, "encryption", &jobj1))
+ log_std(cd, "\tcipher: %s\n", json_object_get_string(jobj1));
+
+ if (json_object_object_get_ex(jobj_segment, "sector_size", &jobj1))
+ log_std(cd, "\tsector: %" PRIu32 " [bytes]\n", json_object_get_uint32(jobj1));
+
+ if (json_object_object_get_ex(jobj_segment, "integrity", &jobj1) &&
+ json_object_object_get_ex(jobj1, "type", &jobj2))
+ log_std(cd, "\tintegrity: %s\n", json_object_get_string(jobj2));
+
+ if (json_object_object_get_ex(jobj_segment, "flags", &jobj1) &&
+ (flags = (int)json_object_array_length(jobj1)) > 0) {
+ jobj2 = json_object_array_get_idx(jobj1, 0);
+ log_std(cd, "\tflags : %s", json_object_get_string(jobj2));
+ for (j = 1; j < flags; j++) {
+ jobj2 = json_object_array_get_idx(jobj1, j);
+ log_std(cd, ", %s", json_object_get_string(jobj2));
+ }
+ log_std(cd, "\n");
+ }
+
+ log_std(cd, "\n");
+ }
+}
+
+static void hdr_dump_digests(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ char key[16];
+ json_object *jobj1, *jobj2, *val;
+ const char *tmps;
+ int i;
+
+ log_std(cd, "Digests:\n");
+ json_object_object_get_ex(hdr_jobj, "digests", &jobj1);
+
+ for (i = 0; i < LUKS2_DIGEST_MAX; i++) {
+ (void) snprintf(key, sizeof(key), "%i", i);
+ json_object_object_get_ex(jobj1, key, &val);
+ if (!val)
+ continue;
+
+ json_object_object_get_ex(val, "type", &jobj2);
+ tmps = json_object_get_string(jobj2);
+ log_std(cd, " %s: %s\n", key, tmps);
+
+ LUKS2_digest_dump(cd, i);
+ }
+}
+
+int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+ if (!hdr->jobj)
+ return -EINVAL;
+
+ JSON_DBG(cd, hdr->jobj, NULL);
+
+ log_std(cd, "LUKS header information\n");
+ log_std(cd, "Version: \t%u\n", hdr->version);
+ log_std(cd, "Epoch: \t%" PRIu64 "\n", hdr->seqid);
+ log_std(cd, "Metadata area: \t%" PRIu64 " [bytes]\n", LUKS2_metadata_size(hdr->jobj));
+ log_std(cd, "Keyslots area: \t%" PRIu64 " [bytes]\n", LUKS2_keyslots_size(hdr->jobj));
+ log_std(cd, "UUID: \t%s\n", *hdr->uuid ? hdr->uuid : "(no UUID)");
+ log_std(cd, "Label: \t%s\n", *hdr->label ? hdr->label : "(no label)");
+ log_std(cd, "Subsystem: \t%s\n", *hdr->subsystem ? hdr->subsystem : "(no subsystem)");
+
+ hdr_dump_config(cd, hdr->jobj);
+ hdr_dump_segments(cd, hdr->jobj);
+ hdr_dump_keyslots(cd, hdr->jobj);
+ hdr_dump_tokens(cd, hdr->jobj);
+ hdr_dump_digests(cd, hdr->jobj);
+
+ return 0;
+}
+
+uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr)
+{
+ json_object *jobj1;
+
+ if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj1))
+ return 0;
+
+ return get_first_data_offset(jobj1, "crypt") / SECTOR_SIZE;
+}
+
+const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment)
+{
+ json_object *jobj1, *jobj2, *jobj3;
+ char buf[16];
+
+ if (segment < 0 || snprintf(buf, sizeof(buf), "%u", segment) < 1)
+ return NULL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj1))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, buf, &jobj2))
+ return NULL;
+
+ if (json_object_object_get_ex(jobj2, "encryption", &jobj3))
+ return json_object_get_string(jobj3);
+
+ /* FIXME: default encryption (for other segment types) must be string here. */
+ return "null";
+
+}
+
+const char *LUKS2_get_keyslot_cipher(struct luks2_hdr *hdr, int keyslot, size_t *key_size)
+{
+ json_object *jobj_keyslot, *jobj_area, *jobj1;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return NULL;
+
+ /* currently we only support raw length preserving area encryption */
+ json_object_object_get_ex(jobj_area, "type", &jobj1);
+ if (strcmp(json_object_get_string(jobj1), "raw"))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj_area, "key_size", &jobj1))
+ return NULL;
+ *key_size = json_object_get_int(jobj1);
+
+ if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1))
+ return NULL;
+
+ return json_object_get_string(jobj1);
+}
+
+const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment)
+{
+ json_object *jobj1, *jobj2, *jobj3, *jobj4;
+ char buf[16];
+
+ if (segment < 0 || snprintf(buf, sizeof(buf), "%u", segment) < 1)
+ return NULL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj1))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, buf, &jobj2))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj2, "integrity", &jobj3))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj3, "type", &jobj4))
+ return NULL;
+
+ return json_object_get_string(jobj4);
+}
+
+/* FIXME: this only ensures that once we have journal encryption, it is not ignored. */
+static int LUKS2_integrity_compatible(struct luks2_hdr *hdr)
+{
+ json_object *jobj1, *jobj2, *jobj3, *jobj4;
+ const char *str;
+
+ if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj1))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj1, CRYPT_DEFAULT_SEGMENT_STR, &jobj2))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj2, "integrity", &jobj3))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj3, "journal_encryption", &jobj4) ||
+ !(str = json_object_get_string(jobj4)) ||
+ strcmp(str, "none"))
+ return 0;
+
+ if (!json_object_object_get_ex(jobj3, "journal_integrity", &jobj4) ||
+ !(str = json_object_get_string(jobj4)) ||
+ strcmp(str, "none"))
+ return 0;
+
+ return 1;
+}
+
+static int LUKS2_keyslot_get_volume_key_size(struct luks2_hdr *hdr, const char *keyslot)
+{
+ json_object *jobj1, *jobj2, *jobj3;
+
+ if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj1))
+ return -1;
+
+ if (!json_object_object_get_ex(jobj1, keyslot, &jobj2))
+ return -1;
+
+ if (!json_object_object_get_ex(jobj2, "key_size", &jobj3))
+ return -1;
+
+ return json_object_get_int(jobj3);
+}
+
+/* Key size used for encryption of keyslot */
+int LUKS2_get_keyslot_stored_key_size(struct luks2_hdr *hdr, int keyslot)
+{
+ char keyslot_name[16];
+
+ if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
+ return -1;
+
+ return LUKS2_keyslot_get_volume_key_size(hdr, keyslot_name);
+}
+
+int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment)
+{
+ json_object *jobj_digests, *jobj_digest_segments, *jobj_digest_keyslots, *jobj1;
+ char buf[16];
+
+ if (snprintf(buf, sizeof(buf), "%u", segment) < 1)
+ return -1;
+
+ json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ UNUSED(key);
+ json_object_object_get_ex(val, "segments", &jobj_digest_segments);
+ json_object_object_get_ex(val, "keyslots", &jobj_digest_keyslots);
+
+ if (!LUKS2_array_jobj(jobj_digest_segments, buf))
+ continue;
+ if (json_object_array_length(jobj_digest_keyslots) <= 0)
+ continue;
+
+ jobj1 = json_object_array_get_idx(jobj_digest_keyslots, 0);
+
+ return LUKS2_keyslot_get_volume_key_size(hdr, json_object_get_string(jobj1));
+ }
+
+ return -1;
+}
+
+int LUKS2_get_sector_size(struct luks2_hdr *hdr)
+{
+ json_object *jobj1, *jobj_segment;
+
+ jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT);
+ if (!jobj_segment)
+ return SECTOR_SIZE;
+
+ json_object_object_get_ex(jobj_segment, "sector_size", &jobj1);
+ if (!jobj1)
+ return SECTOR_SIZE;
+
+ return json_object_get_int(jobj1);
+}
+
+int LUKS2_activate(struct crypt_device *cd,
+ const char *name,
+ struct volume_key *vk,
+ uint32_t flags)
+{
+ int r;
+ struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ struct crypt_dm_active_device dmdi = {}, dmd = {
+ .uuid = crypt_get_uuid(cd),
+ };
+
+ /* do not allow activation when particular requirements detected */
+ if ((r = LUKS2_unmet_requirements(cd, hdr, 0, 0)))
+ return r;
+
+ r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
+ vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd),
+ crypt_get_data_offset(cd), crypt_get_integrity(cd) ?: "none",
+ crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd));
+ if (r < 0)
+ return r;
+
+ /* Add persistent activation flags */
+ if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT))
+ LUKS2_config_get_flags(cd, hdr, &dmd.flags);
+
+ dmd.flags |= flags;
+
+ if (crypt_get_integrity_tag_size(cd)) {
+ if (!LUKS2_integrity_compatible(hdr)) {
+ log_err(cd, "Unsupported device integrity configuration.");
+ return -EINVAL;
+ }
+
+ r = INTEGRITY_create_dmd_device(cd, NULL, NULL, NULL, NULL, &dmdi, dmd.flags);
+ if (r)
+ return r;
+
+ dmd.segment.u.crypt.offset = 0;
+ dmd.segment.size = dmdi.segment.size;
+
+ r = create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi);
+ } else
+ r = create_or_reload_device(cd, name, CRYPT_LUKS2, &dmd);
+
+ dm_targets_free(cd, &dmd);
+ dm_targets_free(cd, &dmdi);
+
+ return r;
+}
+
+int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet)
+{
+ uint32_t reqs;
+ int r = LUKS2_config_get_requirements(cd, hdr, &reqs);
+
+ if (r) {
+ if (!quiet)
+ log_err(cd, _("Failed to read LUKS2 requirements."));
+ return r;
+ }
+
+ /* do not mask unknown requirements check */
+ if (reqs_unknown(reqs)) {
+ if (!quiet)
+ log_err(cd, _("Unmet LUKS2 requirements detected."));
+ return -ETXTBSY;
+ }
+
+ /* mask out permitted requirements */
+ reqs &= ~reqs_mask;
+
+ if (reqs_reencrypt(reqs) && !quiet)
+ log_err(cd, _("Offline reencryption in progress. Aborting."));
+
+ /* any remaining unmasked requirement fails the check */
+ return reqs ? -EINVAL : 0;
+}
+
+/*
+ * NOTE: this routine is called on json object that failed validation.
+ * Proceed with caution :)
+ *
+ * known glitches so far:
+ *
+ * any version < 2.0.3:
+ * - luks2 keyslot pbkdf params change via crypt_keyslot_change_by_passphrase()
+ * could leave previous type parameters behind. Correct this by purging
+ * all params not needed by current type.
+ */
+void LUKS2_hdr_repair(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ json_object *jobj_keyslots;
+
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+ return;
+ if (!json_object_is_type(jobj_keyslots, json_type_object))
+ return;
+
+ LUKS2_keyslots_repair(cd, jobj_keyslots);
+}
+
+void json_object_object_del_by_uint(json_object *jobj, unsigned key)
+{
+ char key_name[16];
+
+ if (snprintf(key_name, sizeof(key_name), "%u", key) < 1)
+ return;
+ json_object_object_del(jobj, key_name);
+}
+
+int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val)
+{
+ char key_name[16];
+
+ if (snprintf(key_name, sizeof(key_name), "%u", key) < 1)
+ return -EINVAL;
+
+#if HAVE_DECL_JSON_OBJECT_OBJECT_ADD_EX
+ return json_object_object_add_ex(jobj, key_name, jobj_val, 0) ? -ENOMEM : 0;
+#else
+ json_object_object_add(jobj, key_name, jobj_val);
+ return 0;
+#endif
+}
diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c
new file mode 100644
index 0000000..b897ba3
--- /dev/null
+++ b/lib/luks2/luks2_keyslot.c
@@ -0,0 +1,663 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, keyslot handling
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+
+/* Internal implementations */
+extern const keyslot_handler luks2_keyslot;
+
+static const keyslot_handler *keyslot_handlers[LUKS2_KEYSLOTS_MAX] = {
+ &luks2_keyslot,
+ NULL
+};
+
+static const keyslot_handler
+*LUKS2_keyslot_handler_type(struct crypt_device *cd, const char *type)
+{
+ int i;
+
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX && keyslot_handlers[i]; i++) {
+ if (!strcmp(keyslot_handlers[i]->name, type))
+ return keyslot_handlers[i];
+ }
+
+ return NULL;
+}
+
+static const keyslot_handler
+*LUKS2_keyslot_handler(struct crypt_device *cd, int keyslot)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj1, *jobj2;
+
+ if (keyslot < 0)
+ return NULL;
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return NULL;
+
+ if (!(jobj1 = LUKS2_get_keyslot_jobj(hdr, keyslot)))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, "type", &jobj2))
+ return NULL;
+
+ return LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj2));
+}
+
+int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type)
+{
+ int i;
+
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
+ if (!LUKS2_get_keyslot_jobj(hdr, i))
+ return i;
+
+ return -EINVAL;
+}
+
+/* Check if a keyslot is asssigned to specific segment */
+int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
+{
+ int keyslot_digest, segment_digest;
+
+ /* no need to check anything */
+ if (segment == CRYPT_ANY_SEGMENT)
+ return 0;
+
+ keyslot_digest = LUKS2_digest_by_keyslot(hdr, keyslot);
+ if (keyslot_digest < 0)
+ return -EINVAL;
+
+ segment_digest = LUKS2_digest_by_segment(hdr, segment);
+ if (segment_digest < 0)
+ return segment_digest;
+
+ return segment_digest == keyslot_digest ? 0 : -ENOENT;
+}
+
+/* Number of keyslots assigned to a segment or all keyslots for CRYPT_ANY_SEGMENT */
+int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment)
+{
+ int num = 0;
+ json_object *jobj_keyslots;
+
+ json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
+
+ json_object_object_foreach(jobj_keyslots, slot, val) {
+ UNUSED(val);
+ if (!LUKS2_keyslot_for_segment(hdr, atoi(slot), segment))
+ num++;
+ }
+
+ return num;
+}
+
+int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *cipher_spec)
+{
+ char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
+
+ if (!cipher_spec || !strcmp(cipher_spec, "null") || !strcmp(cipher_spec, "cipher_null"))
+ return 1;
+
+ if (crypt_parse_name_and_mode(cipher_spec, cipher, NULL, cipher_mode) < 0)
+ return 1;
+
+ /* Keyslot is already authenticated; we cannot use integrity tags here */
+ if (crypt_get_integrity_tag_size(cd))
+ return 1;
+
+ /* Wrapped key schemes cannot be used for keyslot encryption */
+ if (crypt_cipher_wrapped_key(cipher, cipher_mode))
+ return 1;
+
+ /* Check if crypto backend can use the cipher */
+ if (crypt_cipher_ivsize(cipher, cipher_mode) < 0)
+ return 1;
+
+ return 0;
+}
+
+int LUKS2_keyslot_params_default(struct crypt_device *cd, struct luks2_hdr *hdr,
+ struct luks2_keyslot_params *params)
+{
+ const struct crypt_pbkdf_type *pbkdf = crypt_get_pbkdf_type(cd);
+ const char *cipher_spec;
+ size_t key_size;
+ int r;
+
+ if (!hdr || !pbkdf || !params)
+ return -EINVAL;
+
+ /*
+ * set keyslot area encryption parameters
+ */
+ params->area_type = LUKS2_KEYSLOT_AREA_RAW;
+ cipher_spec = crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size);
+ if (!cipher_spec || !key_size)
+ return -EINVAL;
+
+ params->area.raw.key_size = key_size;
+ r = snprintf(params->area.raw.encryption, sizeof(params->area.raw.encryption), "%s", cipher_spec);
+ if (r < 0 || (size_t)r >= sizeof(params->area.raw.encryption))
+ return -EINVAL;
+
+ /*
+ * set keyslot AF parameters
+ */
+ params->af_type = LUKS2_KEYSLOT_AF_LUKS1;
+ /* currently we use hash for AF from pbkdf settings */
+ r = snprintf(params->af.luks1.hash, sizeof(params->af.luks1.hash), "%s", pbkdf->hash);
+ if (r < 0 || (size_t)r >= sizeof(params->af.luks1.hash))
+ return -EINVAL;
+ params->af.luks1.stripes = 4000;
+
+ return 0;
+}
+
+int LUKS2_keyslot_pbkdf(struct luks2_hdr *hdr, int keyslot, struct crypt_pbkdf_type *pbkdf)
+{
+ json_object *jobj_keyslot, *jobj_kdf, *jobj;
+
+ if (!hdr || !pbkdf)
+ return -EINVAL;
+
+ if (LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID)
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -ENOENT;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_kdf, "type", &jobj))
+ return -EINVAL;
+
+ memset(pbkdf, 0, sizeof(*pbkdf));
+
+ pbkdf->type = json_object_get_string(jobj);
+ if (json_object_object_get_ex(jobj_kdf, "hash", &jobj))
+ pbkdf->hash = json_object_get_string(jobj);
+ if (json_object_object_get_ex(jobj_kdf, "iterations", &jobj))
+ pbkdf->iterations = json_object_get_int(jobj);
+ if (json_object_object_get_ex(jobj_kdf, "time", &jobj))
+ pbkdf->iterations = json_object_get_int(jobj);
+ if (json_object_object_get_ex(jobj_kdf, "memory", &jobj))
+ pbkdf->max_memory_kb = json_object_get_int(jobj);
+ if (json_object_object_get_ex(jobj_kdf, "cpus", &jobj))
+ pbkdf->parallel_threads = json_object_get_int(jobj);
+
+ return 0;
+}
+
+static int LUKS2_keyslot_unbound(struct luks2_hdr *hdr, int keyslot)
+{
+ json_object *jobj_digest, *jobj_segments;
+ int digest = LUKS2_digest_by_keyslot(hdr, keyslot);
+
+ if (digest < 0)
+ return 0;
+
+ if (!(jobj_digest = LUKS2_get_digest_jobj(hdr, digest)))
+ return 0;
+
+ json_object_object_get_ex(jobj_digest, "segments", &jobj_segments);
+ if (!jobj_segments || !json_object_is_type(jobj_segments, json_type_array) ||
+ json_object_array_length(jobj_segments) == 0)
+ return 1;
+
+ return 0;
+}
+
+crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot)
+{
+ if(keyslot >= LUKS2_KEYSLOTS_MAX || keyslot < 0)
+ return CRYPT_SLOT_INVALID;
+
+ if (!LUKS2_get_keyslot_jobj(hdr, keyslot))
+ return CRYPT_SLOT_INACTIVE;
+
+ if (LUKS2_keyslot_unbound(hdr, keyslot))
+ return CRYPT_SLOT_UNBOUND;
+
+ if (LUKS2_keyslot_active_count(hdr, CRYPT_DEFAULT_SEGMENT) == 1 &&
+ !LUKS2_keyslot_for_segment(hdr, keyslot, CRYPT_DEFAULT_SEGMENT))
+ return CRYPT_SLOT_ACTIVE_LAST;
+
+ return CRYPT_SLOT_ACTIVE;
+}
+
+int LUKS2_keyslot_area(struct luks2_hdr *hdr,
+ int keyslot,
+ uint64_t *offset,
+ uint64_t *length)
+{
+ json_object *jobj_keyslot, *jobj_area, *jobj;
+
+ if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID)
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -ENOENT;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_area, "offset", &jobj))
+ return -EINVAL;
+ *offset = json_object_get_int64(jobj);
+
+ if (!json_object_object_get_ex(jobj_area, "size", &jobj))
+ return -EINVAL;
+ *length = json_object_get_int64(jobj);
+
+ return 0;
+}
+
+static int LUKS2_open_and_verify(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int segment,
+ const char *password,
+ size_t password_len,
+ struct volume_key **vk)
+{
+ const keyslot_handler *h;
+ int key_size, r;
+
+ if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
+ return -ENOENT;
+
+ r = h->validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot));
+ if (r) {
+ log_dbg(cd, "Keyslot %d validation failed.", keyslot);
+ return r;
+ }
+
+ r = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
+ if (r) {
+ if (r == -ENOENT)
+ log_dbg(cd, "Keyslot %d unusable for segment %d.", keyslot, segment);
+ return r;
+ }
+
+ key_size = LUKS2_get_volume_key_size(hdr, segment);
+ if (key_size < 0)
+ key_size = LUKS2_get_keyslot_stored_key_size(hdr, keyslot);
+ if (key_size < 0)
+ return -EINVAL;
+
+ *vk = crypt_alloc_volume_key(key_size, NULL);
+ if (!*vk)
+ return -ENOMEM;
+
+ r = h->open(cd, keyslot, password, password_len, (*vk)->key, (*vk)->keylength);
+ if (r < 0)
+ log_dbg(cd, "Keyslot %d (%s) open failed with %d.", keyslot, h->name, r);
+ else
+ r = LUKS2_digest_verify(cd, hdr, *vk, keyslot);
+
+ if (r < 0) {
+ crypt_free_volume_key(*vk);
+ *vk = NULL;
+ }
+
+ return r < 0 ? r : keyslot;
+}
+
+static int LUKS2_keyslot_open_priority(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ crypt_keyslot_priority priority,
+ const char *password,
+ size_t password_len,
+ int segment,
+ struct volume_key **vk)
+{
+ json_object *jobj_keyslots, *jobj;
+ crypt_keyslot_priority slot_priority;
+ int keyslot, r = -ENOENT;
+
+ json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
+
+ json_object_object_foreach(jobj_keyslots, slot, val) {
+ if (!json_object_object_get_ex(val, "priority", &jobj))
+ slot_priority = CRYPT_SLOT_PRIORITY_NORMAL;
+ else
+ slot_priority = json_object_get_int(jobj);
+
+ keyslot = atoi(slot);
+ if (slot_priority != priority) {
+ log_dbg(cd, "Keyslot %d priority %d != %d (required), skipped.",
+ keyslot, slot_priority, priority);
+ continue;
+ }
+
+ r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
+
+ /* Do not retry for errors that are no -EPERM or -ENOENT,
+ former meaning password wrong, latter key slot unusable for segment */
+ if ((r != -EPERM) && (r != -ENOENT))
+ break;
+ }
+
+ return r;
+}
+
+int LUKS2_keyslot_open(struct crypt_device *cd,
+ int keyslot,
+ int segment,
+ const char *password,
+ size_t password_len,
+ struct volume_key **vk)
+{
+ struct luks2_hdr *hdr;
+ int r_prio, r = -EINVAL;
+
+ hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+
+ if (keyslot == CRYPT_ANY_SLOT) {
+ r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
+ password, password_len, segment, vk);
+ if (r_prio >= 0)
+ r = r_prio;
+ else if (r_prio != -EPERM && r_prio != -ENOENT)
+ r = r_prio;
+ else
+ r = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL,
+ password, password_len, segment, vk);
+ /* Prefer password wrong to no entry from priority slot */
+ if (r_prio == -EPERM && r == -ENOENT)
+ r = r_prio;
+ } else
+ r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
+
+ return r;
+}
+
+int LUKS2_keyslot_store(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ const char *password,
+ size_t password_len,
+ const struct volume_key *vk,
+ const struct luks2_keyslot_params *params)
+{
+ const keyslot_handler *h;
+ int r;
+
+ if (keyslot == CRYPT_ANY_SLOT)
+ return -EINVAL;
+
+ if (!LUKS2_get_keyslot_jobj(hdr, keyslot)) {
+ /* Try to allocate default and empty keyslot type */
+ h = LUKS2_keyslot_handler_type(cd, "luks2");
+ if (!h)
+ return -EINVAL;
+
+ r = h->alloc(cd, keyslot, vk->keylength, params);
+ if (r)
+ return r;
+ } else {
+ if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
+ return -EINVAL;
+
+ r = h->update(cd, keyslot, params);
+ if (r) {
+ log_dbg(cd, "Failed to update keyslot %d json.", keyslot);
+ return r;
+ }
+ }
+
+ r = h->validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot));
+ if (r) {
+ log_dbg(cd, "Keyslot validation failed.");
+ return r;
+ }
+
+ return h->store(cd, keyslot, password, password_len,
+ vk->key, vk->keylength);
+}
+
+int LUKS2_keyslot_wipe(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int wipe_area_only)
+{
+ struct device *device = crypt_metadata_device(cd);
+ uint64_t area_offset, area_length;
+ int r;
+ json_object *jobj_keyslot, *jobj_keyslots;
+ const keyslot_handler *h;
+
+ h = LUKS2_keyslot_handler(cd, keyslot);
+
+ if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -ENOENT;
+
+ if (wipe_area_only)
+ log_dbg(cd, "Wiping keyslot %d area only.", keyslot);
+
+ /* Just check that nobody uses the metadata now */
+ r = device_write_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire write lock on device %s."),
+ device_path(device));
+ return r;
+ }
+ device_write_unlock(cd, device);
+
+ /* secure deletion of possible key material in keyslot area */
+ r = crypt_keyslot_area(cd, keyslot, &area_offset, &area_length);
+ if (r && r != -ENOENT)
+ return r;
+
+ /* We can destroy the binary keyslot area now without lock */
+ if (!r) {
+ r = crypt_wipe_device(cd, device, CRYPT_WIPE_SPECIAL, area_offset,
+ area_length, area_length, NULL, NULL);
+ if (r) {
+ if (r == -EACCES) {
+ log_err(cd, _("Cannot write to device %s, permission denied."),
+ device_path(device));
+ r = -EINVAL;
+ } else
+ log_err(cd, _("Cannot wipe device %s."), device_path(device));
+ return r;
+ }
+ }
+
+ if (wipe_area_only)
+ return r;
+
+ /* Slot specific wipe */
+ if (h) {
+ r = h->wipe(cd, keyslot);
+ if (r < 0)
+ return r;
+ } else
+ log_dbg(cd, "Wiping keyslot %d without specific-slot handler loaded.", keyslot);
+
+ json_object_object_del_by_uint(jobj_keyslots, keyslot);
+
+ return LUKS2_hdr_write(cd, hdr);
+}
+
+int LUKS2_keyslot_dump(struct crypt_device *cd, int keyslot)
+{
+ const keyslot_handler *h;
+
+ if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
+ return -EINVAL;
+
+ return h->dump(cd, keyslot);
+}
+
+crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
+ struct luks2_hdr *hdr, int keyslot)
+{
+ json_object *jobj_keyslot, *jobj_priority;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return CRYPT_SLOT_PRIORITY_INVALID;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "priority", &jobj_priority))
+ return CRYPT_SLOT_PRIORITY_NORMAL;
+
+ return json_object_get_int(jobj_priority);
+}
+
+int LUKS2_keyslot_priority_set(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, crypt_keyslot_priority priority, int commit)
+{
+ json_object *jobj_keyslot;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ if (priority == CRYPT_SLOT_PRIORITY_NORMAL)
+ json_object_object_del(jobj_keyslot, "priority");
+ else
+ json_object_object_add(jobj_keyslot, "priority", json_object_new_int(priority));
+
+ return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+}
+
+int placeholder_keyslot_alloc(struct crypt_device *cd,
+ int keyslot,
+ uint64_t area_offset,
+ uint64_t area_length,
+ size_t volume_key_len)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj_keyslots, *jobj_keyslot, *jobj_area;
+
+ log_dbg(cd, "Allocating placeholder keyslot %d for LUKS1 down conversion.", keyslot);
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX)
+ return -EINVAL;
+
+ if (LUKS2_get_keyslot_jobj(hdr, keyslot))
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
+ return -EINVAL;
+
+ jobj_keyslot = json_object_new_object();
+ json_object_object_add(jobj_keyslot, "type", json_object_new_string("placeholder"));
+ /*
+ * key_size = -1 makes placeholder keyslot impossible to pass validation.
+ * It's a safeguard against accidentally storing temporary conversion
+ * LUKS2 header.
+ */
+ json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(-1));
+
+ /* Area object */
+ jobj_area = json_object_new_object();
+ json_object_object_add(jobj_area, "offset", json_object_new_uint64(area_offset));
+ json_object_object_add(jobj_area, "size", json_object_new_uint64(area_length));
+ json_object_object_add(jobj_keyslot, "area", jobj_area);
+
+ json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+
+ return 0;
+}
+
+static unsigned LUKS2_get_keyslot_digests_count(json_object *hdr_jobj, int keyslot)
+{
+ char num[16];
+ json_object *jobj_digests, *jobj_keyslots;
+ unsigned count = 0;
+
+ if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj_digests))
+ return 0;
+
+ if (snprintf(num, sizeof(num), "%u", keyslot) < 0)
+ return 0;
+
+ json_object_object_foreach(jobj_digests, key, val) {
+ UNUSED(key);
+ json_object_object_get_ex(val, "keyslots", &jobj_keyslots);
+ if (LUKS2_array_jobj(jobj_keyslots, num))
+ count++;
+ }
+
+ return count;
+}
+
+/* run only on header that passed basic format validation */
+int LUKS2_keyslots_validate(struct crypt_device *cd, json_object *hdr_jobj)
+{
+ const keyslot_handler *h;
+ int keyslot;
+ json_object *jobj_keyslots, *jobj_type;
+
+ if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+ return -EINVAL;
+
+ json_object_object_foreach(jobj_keyslots, slot, val) {
+ keyslot = atoi(slot);
+ json_object_object_get_ex(val, "type", &jobj_type);
+ h = LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj_type));
+ if (!h)
+ continue;
+ if (h->validate && h->validate(cd, val)) {
+ log_dbg(cd, "Keyslot type %s validation failed on keyslot %d.", h->name, keyslot);
+ return -EINVAL;
+ }
+
+ if (!strcmp(h->name, "luks2") && LUKS2_get_keyslot_digests_count(hdr_jobj, keyslot) != 1) {
+ log_dbg(cd, "Keyslot %d is not assigned to exactly 1 digest.", keyslot);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+void LUKS2_keyslots_repair(struct crypt_device *cd, json_object *jobj_keyslots)
+{
+ const keyslot_handler *h;
+ json_object *jobj_type;
+
+ json_object_object_foreach(jobj_keyslots, slot, val) {
+ UNUSED(slot);
+ if (!json_object_is_type(val, json_type_object) ||
+ !json_object_object_get_ex(val, "type", &jobj_type) ||
+ !json_object_is_type(jobj_type, json_type_string))
+ continue;
+
+ h = LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj_type));
+ if (h && h->repair)
+ h->repair(cd, val);
+ }
+}
diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c
new file mode 100644
index 0000000..26fe9b1
--- /dev/null
+++ b/lib/luks2/luks2_keyslot_luks2.c
@@ -0,0 +1,785 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+
+/* FIXME: move keyslot encryption to crypto backend */
+#include "../luks1/af.h"
+
+#define LUKS_SALTSIZE 32
+#define LUKS_SLOT_ITERATIONS_MIN 1000
+#define LUKS_STRIPES 4000
+
+static int luks2_encrypt_to_storage(char *src, size_t srcLength,
+ const char *cipher, const char *cipher_mode,
+ struct volume_key *vk, unsigned int sector,
+ struct crypt_device *cd)
+{
+ struct device *device = crypt_metadata_device(cd);
+#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
+ int r = device_write_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire write lock on device %s."), device_path(device));
+ return r;
+ }
+ r = LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd);
+ device_write_unlock(cd, crypt_metadata_device(cd));
+ return r;
+#else
+ struct crypt_storage *s;
+ int devfd = -1, r;
+
+ /* Only whole sector writes supported */
+ if (MISALIGNED_512(srcLength))
+ return -EINVAL;
+
+ /* Encrypt buffer */
+ r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
+ if (r) {
+ log_dbg(cd, "Userspace crypto wrapper cannot use %s-%s (%d).",
+ cipher, cipher_mode, r);
+ return r;
+ }
+
+ r = crypt_storage_encrypt(s, 0, srcLength / SECTOR_SIZE, src);
+ crypt_storage_destroy(s);
+ if (r)
+ return r;
+
+ r = device_write_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire write lock on device %s."),
+ device_path(device));
+ return r;
+ }
+
+ devfd = device_open_locked(cd, device, O_RDWR);
+ if (devfd >= 0) {
+ if (write_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), src,
+ srcLength, sector * SECTOR_SIZE) < 0)
+ r = -EIO;
+ else
+ r = 0;
+
+ device_sync(cd, device, devfd);
+ close(devfd);
+ } else
+ r = -EIO;
+
+ device_write_unlock(cd, device);
+
+ if (r)
+ log_err(cd, _("IO error while encrypting keyslot."));
+
+ return r;
+#endif
+}
+
+static int luks2_decrypt_from_storage(char *dst, size_t dstLength,
+ const char *cipher, const char *cipher_mode, struct volume_key *vk,
+ unsigned int sector, struct crypt_device *cd)
+{
+ struct device *device = crypt_metadata_device(cd);
+#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
+ int r = device_read_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire read lock on device %s."), device_path(device));
+ return r;
+ }
+ r = LUKS_decrypt_from_storage(dst, dstLength, cipher, cipher_mode, vk, sector, cd);
+ device_read_unlock(cd, crypt_metadata_device(cd));
+ return r;
+#else
+ struct crypt_storage *s;
+ int devfd = -1, r;
+
+ /* Only whole sector writes supported */
+ if (MISALIGNED_512(dstLength))
+ return -EINVAL;
+
+ r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
+ if (r) {
+ log_dbg(cd, "Userspace crypto wrapper cannot use %s-%s (%d).",
+ cipher, cipher_mode, r);
+ return r;
+ }
+
+ r = device_read_lock(cd, device);
+ if (r) {
+ log_err(cd, _("Failed to acquire read lock on device %s."),
+ device_path(device));
+ crypt_storage_destroy(s);
+ return r;
+ }
+
+ devfd = device_open_locked(cd, device, O_RDONLY);
+ if (devfd >= 0) {
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), dst,
+ dstLength, sector * SECTOR_SIZE) < 0)
+ r = -EIO;
+ else
+ r = 0;
+ close(devfd);
+ } else
+ r = -EIO;
+
+ device_read_unlock(cd, device);
+
+ /* Decrypt buffer */
+ if (!r)
+ r = crypt_storage_decrypt(s, 0, dstLength / SECTOR_SIZE, dst);
+ else
+ log_err(cd, _("IO error while decrypting keyslot."));
+
+ crypt_storage_destroy(s);
+ return r;
+#endif
+}
+
+static int luks2_keyslot_get_pbkdf_params(json_object *jobj_keyslot,
+ struct crypt_pbkdf_type *pbkdf, char *salt)
+{
+ json_object *jobj_kdf, *jobj1, *jobj2;
+ size_t salt_len;
+
+ if (!jobj_keyslot || !pbkdf)
+ return -EINVAL;
+
+ memset(pbkdf, 0, sizeof(*pbkdf));
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
+ return -EINVAL;
+ pbkdf->type = json_object_get_string(jobj1);
+ if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
+ if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj2))
+ return -EINVAL;
+ pbkdf->hash = json_object_get_string(jobj2);
+ if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj2))
+ return -EINVAL;
+ pbkdf->iterations = json_object_get_int(jobj2);
+ pbkdf->max_memory_kb = 0;
+ pbkdf->parallel_threads = 0;
+ } else {
+ if (!json_object_object_get_ex(jobj_kdf, "time", &jobj2))
+ return -EINVAL;
+ pbkdf->iterations = json_object_get_int(jobj2);
+ if (!json_object_object_get_ex(jobj_kdf, "memory", &jobj2))
+ return -EINVAL;
+ pbkdf->max_memory_kb = json_object_get_int(jobj2);
+ if (!json_object_object_get_ex(jobj_kdf, "cpus", &jobj2))
+ return -EINVAL;
+ pbkdf->parallel_threads = json_object_get_int(jobj2);
+ }
+
+ if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj2))
+ return -EINVAL;
+ salt_len = LUKS_SALTSIZE;
+ if (!base64_decode(json_object_get_string(jobj2),
+ json_object_get_string_len(jobj2),
+ salt, &salt_len))
+ return -EINVAL;
+ if (salt_len != LUKS_SALTSIZE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int luks2_keyslot_set_key(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ const char *password, size_t passwordLen,
+ const char *volume_key, size_t volume_key_len)
+{
+ struct volume_key *derived_key;
+ char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
+ char *AfKey = NULL;
+ const char *af_hash = NULL;
+ size_t AFEKSize, keyslot_key_len;
+ json_object *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
+ uint64_t area_offset;
+ struct crypt_pbkdf_type pbkdf;
+ int r;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
+ !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ /* prevent accidental volume key size change after allocation */
+ if (!json_object_object_get_ex(jobj_keyslot, "key_size", &jobj2))
+ return -EINVAL;
+ if (json_object_get_int(jobj2) != (int)volume_key_len)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
+ return -EINVAL;
+ area_offset = json_object_get_uint64(jobj2);
+
+ if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
+ return -EINVAL;
+ r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
+ if (r < 0)
+ return r;
+
+ if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
+ return -EINVAL;
+ keyslot_key_len = json_object_get_int(jobj2);
+
+ if (!json_object_object_get_ex(jobj_af, "hash", &jobj2))
+ return -EINVAL;
+ af_hash = json_object_get_string(jobj2);
+
+ if (luks2_keyslot_get_pbkdf_params(jobj_keyslot, &pbkdf, salt))
+ return -EINVAL;
+
+ /*
+ * Allocate derived key storage.
+ */
+ derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
+ if (!derived_key)
+ return -ENOMEM;
+ /*
+ * Calculate keyslot content, split and store it to keyslot area.
+ */
+ r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen,
+ salt, LUKS_SALTSIZE,
+ derived_key->key, derived_key->keylength,
+ pbkdf.iterations, pbkdf.max_memory_kb,
+ pbkdf.parallel_threads);
+ if (r < 0) {
+ crypt_free_volume_key(derived_key);
+ return r;
+ }
+
+ // FIXME: verity key_size to AFEKSize
+ AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
+ AfKey = crypt_safe_alloc(AFEKSize);
+ if (!AfKey) {
+ crypt_free_volume_key(derived_key);
+ return -ENOMEM;
+ }
+
+ r = AF_split(cd, volume_key, AfKey, volume_key_len, LUKS_STRIPES, af_hash);
+
+ if (r == 0) {
+ log_dbg(cd, "Updating keyslot area [0x%04x].", (unsigned)area_offset);
+ /* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */
+ r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode,
+ derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
+ }
+
+ crypt_safe_free(AfKey);
+ crypt_free_volume_key(derived_key);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int luks2_keyslot_get_key(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ const char *password, size_t passwordLen,
+ char *volume_key, size_t volume_key_len)
+{
+ struct volume_key *derived_key;
+ struct crypt_pbkdf_type pbkdf;
+ char *AfKey;
+ size_t AFEKSize;
+ const char *af_hash = NULL;
+ char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
+ json_object *jobj2, *jobj_af, *jobj_area;
+ uint64_t area_offset;
+ size_t keyslot_key_len;
+ int r;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ if (luks2_keyslot_get_pbkdf_params(jobj_keyslot, &pbkdf, salt))
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_af, "hash", &jobj2))
+ return -EINVAL;
+ af_hash = json_object_get_string(jobj2);
+
+ if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
+ return -EINVAL;
+ area_offset = json_object_get_uint64(jobj2);
+
+ if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
+ return -EINVAL;
+ r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
+ if (r < 0)
+ return r;
+
+ if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
+ return -EINVAL;
+ keyslot_key_len = json_object_get_int(jobj2);
+
+ /*
+ * Allocate derived key storage space.
+ */
+ derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
+ if (!derived_key)
+ return -ENOMEM;
+
+ AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
+ AfKey = crypt_safe_alloc(AFEKSize);
+ if (!AfKey) {
+ crypt_free_volume_key(derived_key);
+ return -ENOMEM;
+ }
+ /*
+ * Calculate derived key, decrypt keyslot content and merge it.
+ */
+ r = crypt_pbkdf(pbkdf.type, pbkdf.hash, password, passwordLen,
+ salt, LUKS_SALTSIZE,
+ derived_key->key, derived_key->keylength,
+ pbkdf.iterations, pbkdf.max_memory_kb,
+ pbkdf.parallel_threads);
+
+ if (r == 0) {
+ log_dbg(cd, "Reading keyslot area [0x%04x].", (unsigned)area_offset);
+ /* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */
+ r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode,
+ derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
+ }
+
+ if (r == 0)
+ r = AF_merge(cd, AfKey, volume_key, volume_key_len, LUKS_STRIPES, af_hash);
+
+ crypt_free_volume_key(derived_key);
+ crypt_safe_free(AfKey);
+
+ return r;
+}
+
+/*
+ * currently we support update of only:
+ *
+ * - af hash function
+ * - kdf params
+ */
+static int luks2_keyslot_update_json(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ const struct luks2_keyslot_params *params)
+{
+ const struct crypt_pbkdf_type *pbkdf;
+ json_object *jobj_af, *jobj_area, *jobj_kdf;
+ char salt[LUKS_SALTSIZE], *salt_base64 = NULL;
+ int r;
+
+ /* jobj_keyslot is not yet validated */
+
+ if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ /* update area encryption parameters */
+ json_object_object_add(jobj_area, "encryption", json_object_new_string(params->area.raw.encryption));
+ json_object_object_add(jobj_area, "key_size", json_object_new_int(params->area.raw.key_size));
+
+ pbkdf = crypt_get_pbkdf_type(cd);
+ if (!pbkdf)
+ return -EINVAL;
+
+ r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, params->area.raw.key_size);
+ if (r < 0)
+ return r;
+
+ /* refresh whole 'kdf' object */
+ jobj_kdf = json_object_new_object();
+ if (!jobj_kdf)
+ return -ENOMEM;
+ json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
+ if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
+ json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
+ json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
+ } else {
+ json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
+ json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
+ json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
+ }
+ json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
+
+ /*
+ * Regenerate salt and add it in 'kdf' object
+ */
+ r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
+ if (r < 0)
+ return r;
+ base64_encode_alloc(salt, LUKS_SALTSIZE, &salt_base64);
+ if (!salt_base64)
+ return -ENOMEM;
+ json_object_object_add(jobj_kdf, "salt", json_object_new_string(salt_base64));
+ free(salt_base64);
+
+ /* update 'af' hash */
+ json_object_object_add(jobj_af, "hash", json_object_new_string(params->af.luks1.hash));
+
+ JSON_DBG(cd, jobj_keyslot, "Keyslot JSON:");
+ return 0;
+}
+
+static int luks2_keyslot_alloc(struct crypt_device *cd,
+ int keyslot,
+ size_t volume_key_len,
+ const struct luks2_keyslot_params *params)
+{
+ struct luks2_hdr *hdr;
+ uint64_t area_offset, area_length;
+ json_object *jobj_keyslots, *jobj_keyslot, *jobj_af, *jobj_area;
+ int r;
+
+ log_dbg(cd, "Trying to allocate LUKS2 keyslot %d.", keyslot);
+
+ if (!params || params->area_type != LUKS2_KEYSLOT_AREA_RAW ||
+ params->af_type != LUKS2_KEYSLOT_AF_LUKS1) {
+ log_dbg(cd, "Invalid LUKS2 keyslot parameters.");
+ return -EINVAL;
+ }
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ if (keyslot == CRYPT_ANY_SLOT)
+ keyslot = LUKS2_keyslot_find_empty(hdr, "luks2");
+
+ if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX)
+ return -ENOMEM;
+
+ if (LUKS2_get_keyslot_jobj(hdr, keyslot)) {
+ log_dbg(cd, "Cannot modify already active keyslot %d.", keyslot);
+ return -EINVAL;
+ }
+
+ if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
+ return -EINVAL;
+
+ r = LUKS2_find_area_gap(cd, hdr, volume_key_len, &area_offset, &area_length);
+ if (r < 0)
+ return r;
+
+ jobj_keyslot = json_object_new_object();
+ json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2"));
+ json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len));
+
+ /* AF object */
+ jobj_af = json_object_new_object();
+ json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
+ json_object_object_add(jobj_af, "stripes", json_object_new_int(params->af.luks1.stripes));
+ json_object_object_add(jobj_keyslot, "af", jobj_af);
+
+ /* Area object */
+ jobj_area = json_object_new_object();
+ json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
+ json_object_object_add(jobj_area, "offset", json_object_new_uint64(area_offset));
+ json_object_object_add(jobj_area, "size", json_object_new_uint64(area_length));
+ json_object_object_add(jobj_keyslot, "area", jobj_area);
+
+ json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+
+ r = luks2_keyslot_update_json(cd, jobj_keyslot, params);
+
+ if (!r && LUKS2_check_json_size(cd, hdr)) {
+ log_dbg(cd, "Not enough space in header json area for new keyslot.");
+ r = -ENOSPC;
+ }
+
+ if (r)
+ json_object_object_del_by_uint(jobj_keyslots, keyslot);
+
+ return r;
+}
+
+static int luks2_keyslot_open(struct crypt_device *cd,
+ int keyslot,
+ const char *password,
+ size_t password_len,
+ char *volume_key,
+ size_t volume_key_len)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj_keyslot;
+
+ log_dbg(cd, "Trying to open LUKS2 keyslot %d.", keyslot);
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ return luks2_keyslot_get_key(cd, jobj_keyslot,
+ password, password_len,
+ volume_key, volume_key_len);
+}
+
+/*
+ * This function must not modify json.
+ * It's called after luks2 keyslot validation.
+ */
+static int luks2_keyslot_store(struct crypt_device *cd,
+ int keyslot,
+ const char *password,
+ size_t password_len,
+ const char *volume_key,
+ size_t volume_key_len)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj_keyslot;
+ int r;
+
+ log_dbg(cd, "Calculating attributes for LUKS2 keyslot %d.", keyslot);
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ r = luks2_keyslot_set_key(cd, jobj_keyslot,
+ password, password_len,
+ volume_key, volume_key_len);
+ if (r < 0)
+ return r;
+
+ r = LUKS2_hdr_write(cd, hdr);
+ if (r < 0)
+ return r;
+
+ return keyslot;
+}
+
+static int luks2_keyslot_wipe(struct crypt_device *cd, int keyslot)
+{
+ struct luks2_hdr *hdr;
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ /* Remove any reference of deleted keyslot from digests and tokens */
+ LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0);
+ LUKS2_token_assign(cd, hdr, keyslot, CRYPT_ANY_TOKEN, 0, 0);
+
+ return 0;
+}
+
+static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot)
+{
+ json_object *jobj_keyslot, *jobj1, *jobj_kdf, *jobj_af, *jobj_area;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
+ !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_area, "encryption", &jobj1);
+ log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1));
+
+ json_object_object_get_ex(jobj_area, "key_size", &jobj1);
+ log_std(cd, "\tCipher key: %u bits\n", json_object_get_uint32(jobj1) * 8);
+
+ json_object_object_get_ex(jobj_kdf, "type", &jobj1);
+ log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1));
+
+ if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
+ json_object_object_get_ex(jobj_kdf, "hash", &jobj1);
+ log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
+
+ json_object_object_get_ex(jobj_kdf, "iterations", &jobj1);
+ log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_uint64(jobj1));
+ } else {
+ json_object_object_get_ex(jobj_kdf, "time", &jobj1);
+ log_std(cd, "\tTime cost: %" PRIu64 "\n", json_object_get_int64(jobj1));
+
+ json_object_object_get_ex(jobj_kdf, "memory", &jobj1);
+ log_std(cd, "\tMemory: %" PRIu64 "\n", json_object_get_int64(jobj1));
+
+ json_object_object_get_ex(jobj_kdf, "cpus", &jobj1);
+ log_std(cd, "\tThreads: %" PRIu64 "\n", json_object_get_int64(jobj1));
+ }
+ json_object_object_get_ex(jobj_kdf, "salt", &jobj1);
+ log_std(cd, "\tSalt: ");
+ hexprint_base64(cd, jobj1, " ", " ");
+
+
+ json_object_object_get_ex(jobj_af, "stripes", &jobj1);
+ log_std(cd, "\tAF stripes: %u\n", json_object_get_int(jobj1));
+
+ json_object_object_get_ex(jobj_af, "hash", &jobj1);
+ log_std(cd, "\tAF hash: %s\n", json_object_get_string(jobj1));
+
+ json_object_object_get_ex(jobj_area, "offset", &jobj1);
+ log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
+
+ json_object_object_get_ex(jobj_area, "size", &jobj1);
+ log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
+
+ return 0;
+}
+
+static int luks2_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot)
+{
+ json_object *jobj_kdf, *jobj_af, *jobj_area, *jobj1;
+ const char *type;
+ int count;
+
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
+ !json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ count = json_object_object_length(jobj_kdf);
+
+ jobj1 = json_contains(cd, jobj_kdf, "", "kdf section", "type", json_type_string);
+ if (!jobj1)
+ return -EINVAL;
+ type = json_object_get_string(jobj1);
+
+ if (!strcmp(type, CRYPT_KDF_PBKDF2)) {
+ if (count != 4 || /* type, salt, hash, iterations only */
+ !json_contains(cd, jobj_kdf, "kdf type", type, "hash", json_type_string) ||
+ !json_contains(cd, jobj_kdf, "kdf type", type, "iterations", json_type_int) ||
+ !json_contains(cd, jobj_kdf, "kdf type", type, "salt", json_type_string))
+ return -EINVAL;
+ } else if (!strcmp(type, CRYPT_KDF_ARGON2I) || !strcmp(type, CRYPT_KDF_ARGON2ID)) {
+ if (count != 5 || /* type, salt, time, memory, cpus only */
+ !json_contains(cd, jobj_kdf, "kdf type", type, "time", json_type_int) ||
+ !json_contains(cd, jobj_kdf, "kdf type", type, "memory", json_type_int) ||
+ !json_contains(cd, jobj_kdf, "kdf type", type, "cpus", json_type_int) ||
+ !json_contains(cd, jobj_kdf, "kdf type", type, "salt", json_type_string))
+ return -EINVAL;
+ }
+
+ if (!json_object_object_get_ex(jobj_af, "type", &jobj1))
+ return -EINVAL;
+ if (!strcmp(json_object_get_string(jobj1), "luks1")) {
+ if (!json_contains(cd, jobj_af, "", "luks1 af", "hash", json_type_string) ||
+ !json_contains(cd, jobj_af, "", "luks1 af", "stripes", json_type_int))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ // FIXME check numbered
+ if (!json_object_object_get_ex(jobj_area, "type", &jobj1))
+ return -EINVAL;
+ if (!strcmp(json_object_get_string(jobj1), "raw")) {
+ if (!json_contains(cd, jobj_area, "area", "raw type", "encryption", json_type_string) ||
+ !json_contains(cd, jobj_area, "area", "raw type", "key_size", json_type_int) ||
+ !json_contains(cd, jobj_area, "area", "raw type", "offset", json_type_string) ||
+ !json_contains(cd, jobj_area, "area", "raw type", "size", json_type_string))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int luks2_keyslot_update(struct crypt_device *cd,
+ int keyslot,
+ const struct luks2_keyslot_params *params)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj_keyslot;
+ int r;
+
+ log_dbg(cd, "Updating LUKS2 keyslot %d.", keyslot);
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+ r = luks2_keyslot_update_json(cd, jobj_keyslot, params);
+
+ if (!r && LUKS2_check_json_size(cd, hdr)) {
+ log_dbg(cd, "Not enough space in header json area for updated keyslot %d.", keyslot);
+ r = -ENOSPC;
+ }
+
+ return r;
+}
+
+static void luks2_keyslot_repair(struct crypt_device *cd, json_object *jobj_keyslot)
+{
+ const char *type;
+ json_object *jobj_kdf, *jobj_type;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
+ !json_object_is_type(jobj_kdf, json_type_object))
+ return;
+
+ if (!json_object_object_get_ex(jobj_kdf, "type", &jobj_type) ||
+ !json_object_is_type(jobj_type, json_type_string))
+ return;
+
+ type = json_object_get_string(jobj_type);
+
+ if (!strcmp(type, CRYPT_KDF_PBKDF2)) {
+ /* type, salt, hash, iterations only */
+ json_object_object_foreach(jobj_kdf, key, val) {
+ UNUSED(val);
+ if (!strcmp(key, "type") || !strcmp(key, "salt") ||
+ !strcmp(key, "hash") || !strcmp(key, "iterations"))
+ continue;
+ json_object_object_del(jobj_kdf, key);
+ }
+ } else if (!strcmp(type, CRYPT_KDF_ARGON2I) || !strcmp(type, CRYPT_KDF_ARGON2ID)) {
+ /* type, salt, time, memory, cpus only */
+ json_object_object_foreach(jobj_kdf, key, val) {
+ UNUSED(val);
+ if (!strcmp(key, "type") || !strcmp(key, "salt") ||
+ !strcmp(key, "time") || !strcmp(key, "memory") ||
+ !strcmp(key, "cpus"))
+ continue;
+ json_object_object_del(jobj_kdf, key);
+ }
+ }
+}
+
+const keyslot_handler luks2_keyslot = {
+ .name = "luks2",
+ .alloc = luks2_keyslot_alloc,
+ .update = luks2_keyslot_update,
+ .open = luks2_keyslot_open,
+ .store = luks2_keyslot_store,
+ .wipe = luks2_keyslot_wipe,
+ .dump = luks2_keyslot_dump,
+ .validate = luks2_keyslot_validate,
+ .repair = luks2_keyslot_repair
+};
diff --git a/lib/luks2/luks2_luks1_convert.c b/lib/luks2/luks2_luks1_convert.c
new file mode 100644
index 0000000..7884de0
--- /dev/null
+++ b/lib/luks2/luks2_luks1_convert.c
@@ -0,0 +1,863 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
+ *
+ * Copyright (C) 2015-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2019 Ondrej Kozina
+ * Copyright (C) 2015-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 "luks2_internal.h"
+#include "../luks1/luks.h"
+#include "../luks1/af.h"
+
+static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struct json_object **keyslot_object)
+{
+ char *base64_str, cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
+ size_t base64_len;
+ struct json_object *keyslot_obj, *field, *jobj_kdf, *jobj_af, *jobj_area;
+ uint64_t offset, area_size, offs_a, offs_b, length;
+
+ keyslot_obj = json_object_new_object();
+ json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
+ json_object_object_add(keyslot_obj, "key_size", json_object_new_int64(hdr_v1->keyBytes));
+
+ /* KDF */
+ jobj_kdf = json_object_new_object();
+ json_object_object_add(jobj_kdf, "type", json_object_new_string(CRYPT_KDF_PBKDF2));
+ json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
+ json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
+ /* salt field */
+ base64_len = base64_encode_alloc(hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE, &base64_str);
+ if (!base64_str) {
+ json_object_put(keyslot_obj);
+ json_object_put(jobj_kdf);
+ if (!base64_len)
+ return -EINVAL;
+ return -ENOMEM;
+ }
+ field = json_object_new_string_len(base64_str, base64_len);
+ free(base64_str);
+ json_object_object_add(jobj_kdf, "salt", field);
+ json_object_object_add(keyslot_obj, "kdf", jobj_kdf);
+
+ /* AF */
+ jobj_af = json_object_new_object();
+ json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
+ json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
+ /* stripes field ignored, fixed to LUKS_STRIPES (4000) */
+ json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
+ json_object_object_add(keyslot_obj, "af", jobj_af);
+
+ /* Area */
+ jobj_area = json_object_new_object();
+ json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
+
+ /* encryption algorithm field */
+ if (*hdr_v1->cipherMode != '\0') {
+ (void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
+ json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
+ } else
+ json_object_object_add(jobj_area, "encryption", json_object_new_string(hdr_v1->cipherName));
+
+ /* area */
+ if (LUKS_keyslot_area(hdr_v1, 0, &offs_a, &length) ||
+ LUKS_keyslot_area(hdr_v1, 1, &offs_b, &length) ||
+ LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
+ json_object_put(keyslot_obj);
+ json_object_put(jobj_area);
+ return -EINVAL;
+ }
+ area_size = offs_b - offs_a;
+ json_object_object_add(jobj_area, "key_size", json_object_new_int(hdr_v1->keyBytes));
+ json_object_object_add(jobj_area, "offset", json_object_new_uint64(offset));
+ json_object_object_add(jobj_area, "size", json_object_new_uint64(area_size));
+ json_object_object_add(keyslot_obj, "area", jobj_area);
+
+ *keyslot_object = keyslot_obj;
+ return 0;
+}
+
+static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_object **keyslots_object)
+{
+ int keyslot, r;
+ struct json_object *keyslot_obj, *field;
+
+ keyslot_obj = json_object_new_object();
+ if (!keyslot_obj)
+ return -ENOMEM;
+
+ for (keyslot = 0; keyslot < LUKS_NUMKEYS; keyslot++) {
+ if (hdr_v1->keyblock[keyslot].active != LUKS_KEY_ENABLED)
+ continue;
+ r = json_luks1_keyslot(hdr_v1, keyslot, &field);
+ if (r) {
+ json_object_put(keyslot_obj);
+ return r;
+ }
+ json_object_object_add_by_uint(keyslot_obj, keyslot, field);
+ }
+
+ *keyslots_object = keyslot_obj;
+ return 0;
+}
+
+static int json_luks1_segment(const struct luks_phdr *hdr_v1, struct json_object **segment_object)
+{
+ const char *c;
+ char cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
+ struct json_object *segment_obj, *field;
+ uint64_t number;
+
+ segment_obj = json_object_new_object();
+ if (!segment_obj)
+ return -ENOMEM;
+
+ /* type field */
+ field = json_object_new_string("crypt");
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "type", field);
+
+ /* offset field */
+ number = (uint64_t)hdr_v1->payloadOffset * SECTOR_SIZE;
+
+ field = json_object_new_uint64(number);
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "offset", field);
+
+ /* iv_tweak field */
+ field = json_object_new_string("0");
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "iv_tweak", field);
+
+ /* length field */
+ field = json_object_new_string("dynamic");
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "size", field);
+
+ /* cipher field */
+ if (*hdr_v1->cipherMode != '\0') {
+ (void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
+ c = cipher;
+ } else
+ c = hdr_v1->cipherName;
+
+ field = json_object_new_string(c);
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "encryption", field);
+
+ /* block field */
+ field = json_object_new_int(SECTOR_SIZE);
+ if (!field) {
+ json_object_put(segment_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(segment_obj, "sector_size", field);
+
+ *segment_object = segment_obj;
+ return 0;
+}
+
+static int json_luks1_segments(const struct luks_phdr *hdr_v1, struct json_object **segments_object)
+{
+ int r;
+ struct json_object *segments_obj, *field;
+
+ segments_obj = json_object_new_object();
+ if (!segments_obj)
+ return -ENOMEM;
+
+ r = json_luks1_segment(hdr_v1, &field);
+ if (r) {
+ json_object_put(segments_obj);
+ return r;
+ }
+ json_object_object_add_by_uint(segments_obj, CRYPT_DEFAULT_SEGMENT, field);
+
+ *segments_object = segments_obj;
+ return 0;
+}
+
+static int json_luks1_digest(const struct luks_phdr *hdr_v1, struct json_object **digest_object)
+{
+ char keyslot_str[2], *base64_str;
+ int ks;
+ size_t base64_len;
+ struct json_object *digest_obj, *array, *field;
+
+ digest_obj = json_object_new_object();
+ if (!digest_obj)
+ return -ENOMEM;
+
+ /* type field */
+ field = json_object_new_string("pbkdf2");
+ if (!field) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "type", field);
+
+ /* keyslots array */
+ array = json_object_new_array();
+ if (!array) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "keyslots", json_object_get(array));
+
+ for (ks = 0; ks < LUKS_NUMKEYS; ks++) {
+ if (hdr_v1->keyblock[ks].active != LUKS_KEY_ENABLED)
+ continue;
+ (void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks);
+
+ field = json_object_new_string(keyslot_str);
+ if (!field || json_object_array_add(array, field) < 0) {
+ json_object_put(field);
+ json_object_put(array);
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ }
+
+ json_object_put(array);
+
+ /* segments array */
+ array = json_object_new_array();
+ if (!array) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "segments", json_object_get(array));
+
+ field = json_object_new_string("0");
+ if (!field || json_object_array_add(array, field) < 0) {
+ json_object_put(field);
+ json_object_put(array);
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+
+ json_object_put(array);
+
+ /* hash field */
+ field = json_object_new_string(hdr_v1->hashSpec);
+ if (!field) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "hash", field);
+
+ /* salt field */
+ base64_len = base64_encode_alloc(hdr_v1->mkDigestSalt, LUKS_SALTSIZE, &base64_str);
+ if (!base64_str) {
+ json_object_put(digest_obj);
+ if (!base64_len)
+ return -EINVAL;
+ return -ENOMEM;
+ }
+
+ field = json_object_new_string_len(base64_str, base64_len);
+ free(base64_str);
+ if (!field) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "salt", field);
+
+ /* digest field */
+ base64_len = base64_encode_alloc(hdr_v1->mkDigest, LUKS_DIGESTSIZE, &base64_str);
+ if (!base64_str) {
+ json_object_put(digest_obj);
+ if (!base64_len)
+ return -EINVAL;
+ return -ENOMEM;
+ }
+
+ field = json_object_new_string_len(base64_str, base64_len);
+ free(base64_str);
+ if (!field) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "digest", field);
+
+ /* iterations field */
+ field = json_object_new_int64(hdr_v1->mkDigestIterations);
+ if (!field) {
+ json_object_put(digest_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(digest_obj, "iterations", field);
+
+ *digest_object = digest_obj;
+ return 0;
+}
+
+static int json_luks1_digests(const struct luks_phdr *hdr_v1, struct json_object **digests_object)
+{
+ int r;
+ struct json_object *digests_obj, *field;
+
+ digests_obj = json_object_new_object();
+ if (!digests_obj)
+ return -ENOMEM;
+
+ r = json_luks1_digest(hdr_v1, &field);
+ if (r) {
+ json_object_put(digests_obj);
+ return r;
+ }
+ json_object_object_add(digests_obj, "0", field);
+
+ *digests_object = digests_obj;
+ return 0;
+}
+
+static int json_luks1_object(struct luks_phdr *hdr_v1, struct json_object **luks1_object, uint64_t keyslots_size)
+{
+ int r;
+ struct json_object *luks1_obj, *field;
+ uint64_t json_size;
+
+ luks1_obj = json_object_new_object();
+ if (!luks1_obj)
+ return -ENOMEM;
+
+ /* keyslots field */
+ r = json_luks1_keyslots(hdr_v1, &field);
+ if (r) {
+ json_object_put(luks1_obj);
+ return r;
+ }
+ json_object_object_add(luks1_obj, "keyslots", field);
+
+ /* tokens field */
+ field = json_object_new_object();
+ if (!field) {
+ json_object_put(luks1_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(luks1_obj, "tokens", field);
+
+ /* segments field */
+ r = json_luks1_segments(hdr_v1, &field);
+ if (r) {
+ json_object_put(luks1_obj);
+ return r;
+ }
+ json_object_object_add(luks1_obj, "segments", field);
+
+ /* digests field */
+ r = json_luks1_digests(hdr_v1, &field);
+ if (r) {
+ json_object_put(luks1_obj);
+ return r;
+ }
+ json_object_object_add(luks1_obj, "digests", field);
+
+ /* config field */
+ /* anything else? */
+ field = json_object_new_object();
+ if (!field) {
+ json_object_put(luks1_obj);
+ return -ENOMEM;
+ }
+ json_object_object_add(luks1_obj, "config", field);
+
+ json_size = LUKS2_HDR_16K_LEN - LUKS2_HDR_BIN_LEN;
+ json_object_object_add(field, "json_size", json_object_new_uint64(json_size));
+ json_object_object_add(field, "keyslots_size", json_object_new_uint64(keyslots_size));
+
+ *luks1_object = luks1_obj;
+ return 0;
+}
+
+static void move_keyslot_offset(json_object *jobj, int offset_add)
+{
+ json_object *jobj1, *jobj2, *jobj_area;
+ uint64_t offset = 0;
+
+ json_object_object_get_ex(jobj, "keyslots", &jobj1);
+ json_object_object_foreach(jobj1, key, val) {
+ UNUSED(key);
+ json_object_object_get_ex(val, "area", &jobj_area);
+ json_object_object_get_ex(jobj_area, "offset", &jobj2);
+ offset = json_object_get_uint64(jobj2) + offset_add;
+ json_object_object_add(jobj_area, "offset", json_object_new_uint64(offset));
+ }
+}
+
+/* FIXME: return specific error code for partial write error (aka keyslots are gone) */
+static int move_keyslot_areas(struct crypt_device *cd, off_t offset_from,
+ off_t offset_to, size_t buf_size)
+{
+ struct device *device = crypt_metadata_device(cd);
+ void *buf = NULL;
+ int r = -EIO, devfd = -1;
+
+ log_dbg(cd, "Moving keyslot areas of size %zu from %jd to %jd.",
+ buf_size, (intmax_t)offset_from, (intmax_t)offset_to);
+
+ if (posix_memalign(&buf, crypt_getpagesize(), buf_size))
+ return -ENOMEM;
+
+ devfd = device_open(cd, device, O_RDWR);
+ if (devfd == -1) {
+ free(buf);
+ return -EIO;
+ }
+
+ /* This can safely fail (for block devices). It only allocates space if it is possible. */
+ if (posix_fallocate(devfd, offset_to, buf_size))
+ log_dbg(cd, "Preallocation (fallocate) of new keyslot area not available.");
+
+ /* Try to read *new* area to check that area is there (trimmed backup). */
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), buf, buf_size,
+ offset_to)!= (ssize_t)buf_size)
+ goto out;
+
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), buf, buf_size,
+ offset_from)!= (ssize_t)buf_size)
+ goto out;
+
+ if (write_lseek_blockwise(devfd, device_block_size(cd, device),
+ device_alignment(device), buf, buf_size,
+ offset_to) != (ssize_t)buf_size)
+ goto out;
+
+ r = 0;
+out:
+ device_sync(cd, device, devfd);
+ close(devfd);
+ crypt_memzero(buf, buf_size);
+ free(buf);
+
+ return r;
+}
+
+static int luks_header_in_use(struct crypt_device *cd)
+{
+ int r;
+
+ r = lookup_dm_dev_by_uuid(cd, crypt_get_uuid(cd), crypt_get_type(cd));
+ if (r < 0)
+ log_err(cd, _("Can not check status of device with uuid: %s."), crypt_get_uuid(cd));
+
+ return r;
+}
+
+/* Check if there is a luksmeta area (foreign metadata created by the luksmeta package) */
+static int luksmeta_header_present(struct crypt_device *cd, off_t luks1_size)
+{
+ static const uint8_t LM_MAGIC[] = { 'L', 'U', 'K', 'S', 'M', 'E', 'T', 'A' };
+ struct device *device = crypt_metadata_device(cd);
+ void *buf = NULL;
+ int devfd, r = 0;
+
+ if (posix_memalign(&buf, crypt_getpagesize(), sizeof(LM_MAGIC)))
+ return -ENOMEM;
+
+ devfd = device_open(cd, device, O_RDONLY);
+ if (devfd == -1) {
+ free(buf);
+ return -EIO;
+ }
+
+ /* Note: we must not detect failure as problem here, header can be trimmed. */
+ if (read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device),
+ buf, sizeof(LM_MAGIC), luks1_size) == (ssize_t)sizeof(LM_MAGIC) &&
+ !memcmp(LM_MAGIC, buf, sizeof(LM_MAGIC))) {
+ log_err(cd, _("Unable to convert header with LUKSMETA additional metadata."));
+ r = -EBUSY;
+ }
+
+ close(devfd);
+ free(buf);
+ return r;
+}
+
+/* Convert LUKS1 -> LUKS2 */
+int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct luks2_hdr *hdr2)
+{
+ int r;
+ json_object *jobj = NULL;
+ size_t buf_size, buf_offset, luks1_size, luks1_shift = 2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS;
+ uint64_t max_size = crypt_get_data_offset(cd) * SECTOR_SIZE;
+
+ /* for detached headers max size == device size */
+ if (!max_size && (r = device_size(crypt_metadata_device(cd), &max_size)))
+ return r;
+
+ luks1_size = LUKS_device_sectors(hdr1) << SECTOR_SHIFT;
+ luks1_size = size_round_up(luks1_size, LUKS_ALIGN_KEYSLOTS);
+ if (!luks1_size)
+ return -EINVAL;
+
+ if (LUKS_keyslots_offset(hdr1) != (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
+ log_dbg(cd, "Unsupported keyslots material offset: %zu.", LUKS_keyslots_offset(hdr1));
+ return -EINVAL;
+ }
+
+ if (luksmeta_header_present(cd, luks1_size))
+ return -EINVAL;
+
+ log_dbg(cd, "Max size: %" PRIu64 ", LUKS1 (full) header size %zu , required shift: %zu",
+ max_size, luks1_size, luks1_shift);
+ if ((max_size - luks1_size) < luks1_shift) {
+ log_err(cd, _("Unable to move keyslot area. Not enough space."));
+ return -EINVAL;
+ }
+
+ r = json_luks1_object(hdr1, &jobj, max_size - 2 * LUKS2_HDR_16K_LEN);
+ if (r < 0)
+ return r;
+
+ move_keyslot_offset(jobj, luks1_shift);
+
+ // fill hdr2
+ memset(hdr2, 0, sizeof(*hdr2));
+ hdr2->hdr_size = LUKS2_HDR_16K_LEN;
+ hdr2->seqid = 1;
+ hdr2->version = 2;
+ strncpy(hdr2->checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
+ crypt_random_get(cd, (char*)hdr2->salt1, sizeof(hdr2->salt1), CRYPT_RND_SALT);
+ crypt_random_get(cd, (char*)hdr2->salt2, sizeof(hdr2->salt2), CRYPT_RND_SALT);
+ strncpy(hdr2->uuid, crypt_get_uuid(cd), LUKS2_UUID_L-1); /* UUID should be max 36 chars */
+ hdr2->jobj = jobj;
+
+ /*
+ * It duplicates check in LUKS2_hdr_write() but we don't want to move
+ * keyslot areas in case it would fail later
+ */
+ if (max_size < LUKS2_hdr_and_areas_size(hdr2->jobj)) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ if ((r = luks_header_in_use(cd))) {
+ if (r > 0)
+ r = -EBUSY;
+ goto out;
+ }
+
+ // move keyslots 4k -> 32k offset
+ buf_offset = 2 * LUKS2_HDR_16K_LEN;
+ buf_size = luks1_size - LUKS_ALIGN_KEYSLOTS;
+ if ((r = move_keyslot_areas(cd, 8 * SECTOR_SIZE, buf_offset, buf_size)) < 0) {
+ log_err(cd, _("Unable to move keyslot area."));
+ goto out;
+ }
+
+ // Write JSON hdr2
+ r = LUKS2_hdr_write(cd, hdr2);
+out:
+ LUKS2_hdr_free(cd, hdr2);
+
+ return r;
+}
+
+static int keyslot_LUKS1_compatible(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, uint32_t key_size, const char *hash)
+{
+ json_object *jobj_keyslot, *jobj, *jobj_kdf, *jobj_af;
+ uint64_t l2_offset, l2_length;
+ size_t ks_key_size;
+ const char *ks_cipher, *data_cipher;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
+ if (!jobj_keyslot)
+ return 1;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj) ||
+ strcmp(json_object_get_string(jobj), "luks2"))
+ return 0;
+
+ /* Using PBKDF2, this implies memory and parallel is not used. */
+ jobj = NULL;
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
+ !json_object_object_get_ex(jobj_kdf, "type", &jobj) ||
+ strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2) ||
+ !json_object_object_get_ex(jobj_kdf, "hash", &jobj) ||
+ strcmp(json_object_get_string(jobj), hash))
+ return 0;
+
+ jobj = NULL;
+ if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
+ !json_object_object_get_ex(jobj_af, "stripes", &jobj) ||
+ json_object_get_int(jobj) != LUKS_STRIPES)
+ return 0;
+
+ jobj = NULL;
+ if (!json_object_object_get_ex(jobj_af, "hash", &jobj) ||
+ (crypt_hash_size(json_object_get_string(jobj)) < 0) ||
+ strcmp(json_object_get_string(jobj), hash))
+ return 0;
+
+ /* FIXME: should this go to validation code instead (aka invalid luks2 header if assigned to segment 0)? */
+ /* FIXME: check all keyslots are assigned to segment id 0, and segments count == 1 */
+ ks_cipher = LUKS2_get_keyslot_cipher(hdr, keyslot, &ks_key_size);
+ data_cipher = LUKS2_get_cipher(hdr, CRYPT_DEFAULT_SEGMENT);
+ if (!ks_cipher || !data_cipher || key_size != ks_key_size || strcmp(ks_cipher, data_cipher)) {
+ log_dbg(cd, "Cipher in keyslot %d is different from volume key encryption.", keyslot);
+ return 0;
+ }
+
+ if (LUKS2_keyslot_area(hdr, keyslot, &l2_offset, &l2_length))
+ return 0;
+
+ if (l2_length != (size_round_up(AF_split_sectors(key_size, LUKS_STRIPES) * SECTOR_SIZE, 4096))) {
+ log_dbg(cd, "Area length in LUKS2 keyslot (%d) is not compatible with LUKS1", keyslot);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Convert LUKS2 -> LUKS1 */
+int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct luks_phdr *hdr1)
+{
+ size_t buf_size, buf_offset;
+ char cipher[LUKS_CIPHERNAME_L-1], cipher_mode[LUKS_CIPHERMODE_L-1];
+ char digest[LUKS_DIGESTSIZE], digest_salt[LUKS_SALTSIZE];
+ const char *hash;
+ size_t len;
+ json_object *jobj_keyslot, *jobj_digest, *jobj_segment, *jobj_kdf, *jobj_area, *jobj1, *jobj2;
+ uint32_t key_size;
+ int i, r, last_active = 0;
+ uint64_t offset, area_length;
+ char buf[256], luksMagic[] = LUKS_MAGIC;
+
+ jobj_digest = LUKS2_get_digest_jobj(hdr2, 0);
+ if (!jobj_digest)
+ return -EINVAL;
+
+ jobj_segment = LUKS2_get_segment_jobj(hdr2, CRYPT_DEFAULT_SEGMENT);
+ if (!jobj_segment)
+ return -EINVAL;
+
+ json_object_object_get_ex(hdr2->jobj, "digests", &jobj1);
+ if (!json_object_object_get_ex(jobj_digest, "type", &jobj2) ||
+ strcmp(json_object_get_string(jobj2), "pbkdf2") ||
+ json_object_object_length(jobj1) != 1) {
+ log_err(cd, _("Cannot convert to LUKS1 format - key slot digests are not LUKS1 compatible."));
+ return -EINVAL;
+ }
+ if (!json_object_object_get_ex(jobj_digest, "hash", &jobj2))
+ return -EINVAL;
+ hash = json_object_get_string(jobj2);
+
+ r = crypt_parse_name_and_mode(LUKS2_get_cipher(hdr2, CRYPT_DEFAULT_SEGMENT), cipher, NULL, cipher_mode);
+ if (r < 0)
+ return r;
+
+ if (crypt_cipher_wrapped_key(cipher, cipher_mode)) {
+ log_err(cd, _("Cannot convert to LUKS1 format - device uses wrapped key cipher %s."), cipher);
+ return -EINVAL;
+ }
+
+ r = LUKS2_tokens_count(hdr2);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ log_err(cd, _("Cannot convert to LUKS1 format - LUKS2 header contains %u token(s)."), r);
+ return -EINVAL;
+ }
+
+ r = LUKS2_get_volume_key_size(hdr2, 0);
+ if (r < 0)
+ return -EINVAL;
+ key_size = r;
+
+ for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
+ if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE)
+ continue;
+
+ if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INVALID) {
+ log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is in invalid state."), i);
+ return -EINVAL;
+ }
+
+ if (i >= LUKS_NUMKEYS) {
+ log_err(cd, _("Cannot convert to LUKS1 format - slot %u (over maximum slots) is still active."), i);
+ return -EINVAL;
+ }
+
+ if (!keyslot_LUKS1_compatible(cd, hdr2, i, key_size, hash)) {
+ log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is not LUKS1 compatible."), i);
+ return -EINVAL;
+ }
+ }
+
+ memset(hdr1, 0, sizeof(*hdr1));
+
+ for (i = 0; i < LUKS_NUMKEYS; i++) {
+ hdr1->keyblock[i].active = LUKS_KEY_DISABLED;
+ hdr1->keyblock[i].stripes = LUKS_STRIPES;
+
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, i);
+
+ if (jobj_keyslot) {
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+ if (!json_object_object_get_ex(jobj_area, "offset", &jobj1))
+ return -EINVAL;
+ offset = json_object_get_uint64(jobj1);
+ } else {
+ if (LUKS2_find_area_gap(cd, hdr2, key_size, &offset, &area_length))
+ return -EINVAL;
+ /*
+ * We have to create placeholder luks2 keyslots in place of all
+ * inactive keyslots. Otherwise we would allocate all
+ * inactive luks1 keyslots over same binary keyslot area.
+ */
+ if (placeholder_keyslot_alloc(cd, i, offset, area_length, key_size))
+ return -EINVAL;
+ }
+
+ offset /= SECTOR_SIZE;
+ if (offset > UINT32_MAX)
+ return -EINVAL;
+
+ hdr1->keyblock[i].keyMaterialOffset = offset;
+ hdr1->keyblock[i].keyMaterialOffset -=
+ ((2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS) / SECTOR_SIZE);
+
+ if (!jobj_keyslot)
+ continue;
+
+ hdr1->keyblock[i].active = LUKS_KEY_ENABLED;
+ last_active = i;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
+ continue;
+
+ if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj1))
+ continue;
+ hdr1->keyblock[i].passwordIterations = json_object_get_uint32(jobj1);
+
+ if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj1))
+ continue;
+ len = sizeof(buf);
+ memset(buf, 0, len);
+ if (!base64_decode(json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1), buf, &len))
+ continue;
+ if (len > 0 && len != LUKS_SALTSIZE)
+ continue;
+ memcpy(hdr1->keyblock[i].passwordSalt, buf, LUKS_SALTSIZE);
+ }
+
+ if (!jobj_keyslot) {
+ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, last_active);
+ if (!jobj_keyslot)
+ return -EINVAL;
+ }
+
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+ if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1))
+ return -EINVAL;
+ r = crypt_parse_name_and_mode(json_object_get_string(jobj1), cipher, NULL, cipher_mode);
+ if (r < 0)
+ return r;
+
+ strncpy(hdr1->cipherName, cipher, sizeof(hdr1->cipherName) - 1);
+ strncpy(hdr1->cipherMode, cipher_mode, sizeof(hdr1->cipherMode) - 1);
+
+ if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
+ return -EINVAL;
+ if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj1))
+ return -EINVAL;
+ strncpy(hdr1->hashSpec, json_object_get_string(jobj1), sizeof(hdr1->hashSpec) - 1);
+
+ hdr1->keyBytes = key_size;
+
+ if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
+ return -EINVAL;
+ hdr1->mkDigestIterations = json_object_get_uint32(jobj1);
+
+ if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
+ return -EINVAL;
+ len = sizeof(digest);
+ if (!base64_decode(json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1), digest, &len))
+ return -EINVAL;
+ /* We can store full digest here, not only sha1 length */
+ if (len < LUKS_DIGESTSIZE)
+ return -EINVAL;
+ memcpy(hdr1->mkDigest, digest, LUKS_DIGESTSIZE);
+
+ if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
+ return -EINVAL;
+ len = sizeof(digest_salt);
+ if (!base64_decode(json_object_get_string(jobj1),
+ json_object_get_string_len(jobj1), digest_salt, &len))
+ return -EINVAL;
+ if (len != LUKS_SALTSIZE)
+ return -EINVAL;
+ memcpy(hdr1->mkDigestSalt, digest_salt, LUKS_SALTSIZE);
+
+ if (!json_object_object_get_ex(jobj_segment, "offset", &jobj1))
+ return -EINVAL;
+ offset = json_object_get_uint64(jobj1) / SECTOR_SIZE;
+ if (offset > UINT32_MAX)
+ return -EINVAL;
+ /* FIXME: LUKS1 requires offset == 0 || offset >= luks1_hdr_size */
+ hdr1->payloadOffset = offset;
+
+ strncpy(hdr1->uuid, hdr2->uuid, UUID_STRING_L); /* max 36 chars */
+ hdr1->uuid[UUID_STRING_L-1] = '\0';
+
+ memcpy(hdr1->magic, luksMagic, LUKS_MAGIC_L);
+
+ hdr1->version = 1;
+
+ r = luks_header_in_use(cd);
+ if (r)
+ return r > 0 ? -EBUSY : r;
+
+ // move keyslots 32k -> 4k offset
+ buf_offset = 2 * LUKS2_HDR_16K_LEN;
+ buf_size = LUKS2_keyslots_size(hdr2->jobj);
+ r = move_keyslot_areas(cd, buf_offset, 8 * SECTOR_SIZE, buf_size);
+ if (r < 0) {
+ log_err(cd, _("Unable to move keyslot area."));
+ return r;
+ }
+
+ crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO, 0,
+ 8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
+
+ // Write LUKS1 hdr
+ return LUKS_write_phdr(hdr1, cd);
+}
diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c
new file mode 100644
index 0000000..58ba61d
--- /dev/null
+++ b/lib/luks2/luks2_token.c
@@ -0,0 +1,606 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, token handling
+ *
+ * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 <assert.h>
+
+#include "luks2_internal.h"
+
+/* Builtin tokens */
+extern const crypt_token_handler keyring_handler;
+
+static token_handler token_handlers[LUKS2_TOKENS_MAX] = {
+ /* keyring builtin token */
+ {
+ .get = token_keyring_get,
+ .set = token_keyring_set,
+ .h = &keyring_handler
+ },
+};
+
+static int is_builtin_candidate(const char *type)
+{
+ return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN);
+}
+
+int crypt_token_register(const crypt_token_handler *handler)
+{
+ int i;
+
+ if (is_builtin_candidate(handler->name)) {
+ log_dbg(NULL, "'" LUKS2_BUILTIN_TOKEN_PREFIX "' is reserved prefix for builtin tokens.");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++) {
+ if (!strcmp(token_handlers[i].h->name, handler->name)) {
+ log_dbg(NULL, "Keyslot handler %s is already registered.", handler->name);
+ return -EINVAL;
+ }
+ }
+
+ if (i == LUKS2_TOKENS_MAX)
+ return -EINVAL;
+
+ token_handlers[i].h = handler;
+ return 0;
+}
+
+static const token_handler
+*LUKS2_token_handler_type_internal(struct crypt_device *cd, const char *type)
+{
+ int i;
+
+ for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++)
+ if (!strcmp(token_handlers[i].h->name, type))
+ return token_handlers + i;
+
+ return NULL;
+}
+
+static const crypt_token_handler
+*LUKS2_token_handler_type(struct crypt_device *cd, const char *type)
+{
+ const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
+
+ return th ? th->h : NULL;
+}
+
+static const token_handler
+*LUKS2_token_handler_internal(struct crypt_device *cd, int token)
+{
+ struct luks2_hdr *hdr;
+ json_object *jobj1, *jobj2;
+
+ if (token < 0)
+ return NULL;
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return NULL;
+
+ if (!(jobj1 = LUKS2_get_token_jobj(hdr, token)))
+ return NULL;
+
+ if (!json_object_object_get_ex(jobj1, "type", &jobj2))
+ return NULL;
+
+ return LUKS2_token_handler_type_internal(cd, json_object_get_string(jobj2));
+}
+
+static const crypt_token_handler
+*LUKS2_token_handler(struct crypt_device *cd, int token)
+{
+ const token_handler *th = LUKS2_token_handler_internal(cd, token);
+
+ return th ? th->h : NULL;
+}
+
+static int LUKS2_token_find_free(struct luks2_hdr *hdr)
+{
+ int i;
+
+ for (i = 0; i < LUKS2_TOKENS_MAX; i++)
+ if (!LUKS2_get_token_jobj(hdr, i))
+ return i;
+
+ return -EINVAL;
+}
+
+int LUKS2_token_create(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *json,
+ int commit)
+{
+ const crypt_token_handler *h;
+ const token_handler *th;
+ json_object *jobj_tokens, *jobj_type, *jobj;
+ enum json_tokener_error jerr;
+ char num[16];
+
+ if (token == CRYPT_ANY_TOKEN) {
+ if (!json)
+ return -EINVAL;
+ token = LUKS2_token_find_free(hdr);
+ }
+
+ if (token < 0 || token >= LUKS2_TOKENS_MAX)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens))
+ return -EINVAL;
+
+ snprintf(num, sizeof(num), "%d", token);
+
+ /* Remove token */
+ if (!json)
+ json_object_object_del(jobj_tokens, num);
+ else {
+
+ jobj = json_tokener_parse_verbose(json, &jerr);
+ if (!jobj) {
+ log_dbg(cd, "Token JSON parse failed.");
+ return -EINVAL;
+ }
+
+ if (LUKS2_token_validate(cd, hdr->jobj, jobj, num)) {
+ json_object_put(jobj);
+ return -EINVAL;
+ }
+
+ json_object_object_get_ex(jobj, "type", &jobj_type);
+ if (is_builtin_candidate(json_object_get_string(jobj_type))) {
+ th = LUKS2_token_handler_type_internal(cd, json_object_get_string(jobj_type));
+ if (!th || !th->set) {
+ log_dbg(cd, "%s is builtin token candidate with missing handler", json_object_get_string(jobj_type));
+ json_object_put(jobj);
+ return -EINVAL;
+ }
+ h = th->h;
+ } else
+ h = LUKS2_token_handler_type(cd, json_object_get_string(jobj_type));
+
+ if (h && h->validate && h->validate(cd, json)) {
+ json_object_put(jobj);
+ log_dbg(cd, "Token type %s validation failed.", h->name);
+ return -EINVAL;
+ }
+
+ json_object_object_add(jobj_tokens, num, jobj);
+ if (LUKS2_check_json_size(cd, hdr)) {
+ log_dbg(cd, "Not enough space in header json area for new token.");
+ json_object_object_del(jobj_tokens, num);
+ return -ENOSPC;
+ }
+ }
+
+ if (commit)
+ return LUKS2_hdr_write(cd, hdr) ?: token;
+
+ return token;
+}
+
+crypt_token_info LUKS2_token_status(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char **type)
+{
+ const char *tmp;
+ const token_handler *th;
+ json_object *jobj_type, *jobj_token;
+
+ if (token < 0 || token >= LUKS2_TOKENS_MAX)
+ return CRYPT_TOKEN_INVALID;
+
+ if (!(jobj_token = LUKS2_get_token_jobj(hdr, token)))
+ return CRYPT_TOKEN_INACTIVE;
+
+ json_object_object_get_ex(jobj_token, "type", &jobj_type);
+ tmp = json_object_get_string(jobj_type);
+
+ if ((th = LUKS2_token_handler_type_internal(cd, tmp))) {
+ if (type)
+ *type = th->h->name;
+ return th->set ? CRYPT_TOKEN_INTERNAL : CRYPT_TOKEN_EXTERNAL;
+ }
+
+ if (type)
+ *type = tmp;
+
+ return is_builtin_candidate(tmp) ? CRYPT_TOKEN_INTERNAL_UNKNOWN : CRYPT_TOKEN_EXTERNAL_UNKNOWN;
+}
+
+int LUKS2_builtin_token_get(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *type,
+ void *params)
+{
+ const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
+
+ // internal error
+ assert(th && th->get);
+
+ return th->get(LUKS2_get_token_jobj(hdr, token), params) ?: token;
+}
+
+int LUKS2_builtin_token_create(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *type,
+ const void *params,
+ int commit)
+{
+ const token_handler *th;
+ int r;
+ json_object *jobj_token, *jobj_tokens;
+
+ th = LUKS2_token_handler_type_internal(cd, type);
+
+ // at this point all builtin handlers must exist and have validate fn defined
+ assert(th && th->set && th->h->validate);
+
+ if (token == CRYPT_ANY_TOKEN) {
+ if ((token = LUKS2_token_find_free(hdr)) < 0)
+ log_err(cd, _("No free token slot."));
+ }
+ if (token < 0 || token >= LUKS2_TOKENS_MAX)
+ return -EINVAL;
+
+ r = th->set(&jobj_token, params);
+ if (r) {
+ log_err(cd, _("Failed to create builtin token %s."), type);
+ return r;
+ }
+
+ // builtin tokens must produce valid json
+ r = LUKS2_token_validate(cd, hdr->jobj, jobj_token, "new");
+ assert(!r);
+ r = th->h->validate(cd, json_object_to_json_string_ext(jobj_token,
+ JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE));
+ assert(!r);
+
+ json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
+ json_object_object_add_by_uint(jobj_tokens, token, jobj_token);
+ if (LUKS2_check_json_size(cd, hdr)) {
+ log_dbg(cd, "Not enough space in header json area for new %s token.", type);
+ json_object_object_del_by_uint(jobj_tokens, token);
+ return -ENOSPC;
+ }
+
+ if (commit)
+ return LUKS2_hdr_write(cd, hdr) ?: token;
+
+ return token;
+}
+
+static int LUKS2_token_open(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ char **buffer,
+ size_t *buffer_len,
+ void *usrptr)
+{
+ const char *json;
+ const crypt_token_handler *h;
+ int r;
+
+ if (!(h = LUKS2_token_handler(cd, token)))
+ return -ENOENT;
+
+ if (h->validate) {
+ if (LUKS2_token_json_get(cd, hdr, token, &json))
+ return -EINVAL;
+
+ if (h->validate(cd, json)) {
+ log_dbg(cd, "Token %d (%s) validation failed.", token, h->name);
+ return -EINVAL;
+ }
+ }
+
+ r = h->open(cd, token, buffer, buffer_len, usrptr);
+ if (r < 0)
+ log_dbg(cd, "Token %d (%s) open failed with %d.", token, h->name, r);
+
+ return r;
+}
+
+static void LUKS2_token_buffer_free(struct crypt_device *cd,
+ int token,
+ void *buffer,
+ size_t buffer_len)
+{
+ const crypt_token_handler *h = LUKS2_token_handler(cd, token);
+
+ if (h->buffer_free)
+ h->buffer_free(buffer, buffer_len);
+ else {
+ crypt_memzero(buffer, buffer_len);
+ free(buffer);
+ }
+}
+
+static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ int segment,
+ const char *buffer,
+ size_t buffer_len,
+ struct volume_key **vk)
+{
+ const crypt_token_handler *h;
+ json_object *jobj_token, *jobj_token_keyslots, *jobj;
+ const char *num = NULL;
+ int i, r;
+
+ if (!(h = LUKS2_token_handler(cd, token)))
+ return -ENOENT;
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
+ if (!jobj_token_keyslots)
+ return -EINVAL;
+
+ /* Try to open keyslot referenced in token */
+ r = -EINVAL;
+ for (i = 0; i < (int) json_object_array_length(jobj_token_keyslots) && r < 0; i++) {
+ jobj = json_object_array_get_idx(jobj_token_keyslots, i);
+ num = json_object_get_string(jobj);
+ log_dbg(cd, "Trying to open keyslot %s with token %d (type %s).", num, token, h->name);
+ r = LUKS2_keyslot_open(cd, atoi(num), segment, buffer, buffer_len, vk);
+ }
+
+ if (r >= 0 && num)
+ return atoi(num);
+
+ return r;
+}
+
+int LUKS2_token_open_and_activate(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int token,
+ const char *name,
+ uint32_t flags,
+ void *usrptr)
+{
+ int keyslot, r;
+ char *buffer;
+ size_t buffer_len;
+ struct volume_key *vk = NULL;
+
+ r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, usrptr);
+ if (r < 0)
+ return r;
+
+ r = LUKS2_keyslot_open_by_token(cd, hdr, token,
+ (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
+ CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT,
+ buffer, buffer_len, &vk);
+
+ LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
+
+ if (r < 0)
+ return r;
+
+ keyslot = r;
+
+ if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
+ r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot);
+
+ if (r >= 0 && name)
+ r = LUKS2_activate(cd, name, vk, flags);
+
+ if (r < 0 && vk)
+ crypt_drop_keyring_key(cd, vk->key_description);
+ crypt_free_volume_key(vk);
+
+ return r < 0 ? r : keyslot;
+}
+
+int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *name,
+ uint32_t flags)
+{
+ char *buffer;
+ json_object *tokens_jobj;
+ size_t buffer_len;
+ int keyslot, token, r = -EINVAL;
+ struct volume_key *vk = NULL;
+
+ json_object_object_get_ex(hdr->jobj, "tokens", &tokens_jobj);
+
+ json_object_object_foreach(tokens_jobj, slot, val) {
+ UNUSED(val);
+ token = atoi(slot);
+
+ r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, NULL);
+ if (r < 0)
+ continue;
+
+ r = LUKS2_keyslot_open_by_token(cd, hdr, token,
+ (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
+ CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT,
+ buffer, buffer_len, &vk);
+ LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
+ if (r >= 0)
+ break;
+ }
+
+ keyslot = r;
+
+ if (r >= 0 && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
+ r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot);
+
+ if (r >= 0 && name)
+ r = LUKS2_activate(cd, name, vk, flags);
+
+ if (r < 0 && vk)
+ crypt_drop_keyring_key(cd, vk->key_description);
+ crypt_free_volume_key(vk);
+
+ return r < 0 ? r : keyslot;
+}
+
+void LUKS2_token_dump(struct crypt_device *cd, int token)
+{
+ const crypt_token_handler *h;
+ json_object *jobj_token;
+
+ h = LUKS2_token_handler(cd, token);
+ if (h && h->dump) {
+ jobj_token = LUKS2_get_token_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), token);
+ if (jobj_token)
+ h->dump(cd, json_object_to_json_string_ext(jobj_token,
+ JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE));
+ }
+}
+
+int LUKS2_token_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int token, const char **json)
+{
+ json_object *jobj_token;
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -EINVAL;
+
+ *json = json_object_to_json_string_ext(jobj_token,
+ JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
+ return 0;
+}
+
+static int assign_one_keyslot(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int token, int keyslot, int assign)
+{
+ json_object *jobj1, *jobj_token, *jobj_token_keyslots;
+ char num[16];
+
+ log_dbg(cd, "Keyslot %i %s token %i.", keyslot, assign ? "assigned to" : "unassigned from", token);
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
+ if (!jobj_token_keyslots)
+ return -EINVAL;
+
+ snprintf(num, sizeof(num), "%d", keyslot);
+ if (assign) {
+ jobj1 = LUKS2_array_jobj(jobj_token_keyslots, num);
+ if (!jobj1)
+ json_object_array_add(jobj_token_keyslots, json_object_new_string(num));
+ } else {
+ jobj1 = LUKS2_array_remove(jobj_token_keyslots, num);
+ if (jobj1)
+ json_object_object_add(jobj_token, "keyslots", jobj1);
+ }
+
+ return 0;
+}
+
+static int assign_one_token(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, int token, int assign)
+{
+ json_object *jobj_keyslots;
+ int r = 0;
+
+ if (!LUKS2_get_token_jobj(hdr, token))
+ return -EINVAL;
+
+ if (keyslot == CRYPT_ANY_SLOT) {
+ json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
+
+ json_object_object_foreach(jobj_keyslots, key, val) {
+ UNUSED(val);
+ r = assign_one_keyslot(cd, hdr, token, atoi(key), assign);
+ if (r < 0)
+ break;
+ }
+ } else
+ r = assign_one_keyslot(cd, hdr, token, keyslot, assign);
+
+ return r;
+}
+
+int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, int token, int assign, int commit)
+{
+ json_object *jobj_tokens;
+ int r = 0;
+
+ if (token == CRYPT_ANY_TOKEN) {
+ json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
+
+ json_object_object_foreach(jobj_tokens, key, val) {
+ UNUSED(val);
+ r = assign_one_token(cd, hdr, keyslot, atoi(key), assign);
+ if (r < 0)
+ break;
+ }
+ } else
+ r = assign_one_token(cd, hdr, keyslot, token, assign);
+
+ if (r < 0)
+ return r;
+
+ // FIXME: do not write header in nothing changed
+ if (commit)
+ return LUKS2_hdr_write(cd, hdr) ?: token;
+
+ return token;
+}
+
+int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr,
+ int keyslot, int token)
+{
+ int i;
+ json_object *jobj_token, *jobj_token_keyslots, *jobj;
+
+ if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX || token < 0 || token >= LUKS2_TOKENS_MAX)
+ return -EINVAL;
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -ENOENT;
+
+ json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
+
+ for (i = 0; i < (int) json_object_array_length(jobj_token_keyslots); i++) {
+ jobj = json_object_array_get_idx(jobj_token_keyslots, i);
+ if (keyslot == atoi(json_object_get_string(jobj)))
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int LUKS2_tokens_count(struct luks2_hdr *hdr)
+{
+ json_object *jobj_tokens = LUKS2_get_tokens_jobj(hdr);
+ if (!jobj_tokens)
+ return -EINVAL;
+
+ return json_object_object_length(jobj_tokens);
+}
diff --git a/lib/luks2/luks2_token_keyring.c b/lib/luks2/luks2_token_keyring.c
new file mode 100644
index 0000000..7f28607
--- /dev/null
+++ b/lib/luks2/luks2_token_keyring.c
@@ -0,0 +1,170 @@
+/*
+ * LUKS - Linux Unified Key Setup v2, kernel keyring token
+ *
+ * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2019 Ondrej Kozina
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 <assert.h>
+
+#include "luks2_internal.h"
+
+static int keyring_open(struct crypt_device *cd,
+ int token,
+ char **buffer,
+ size_t *buffer_len,
+ void *usrptr __attribute__((unused)))
+{
+ json_object *jobj_token, *jobj_key;
+ struct luks2_hdr *hdr;
+ int r;
+
+ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
+ return -EINVAL;
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_token, "key_description", &jobj_key);
+
+ r = keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len);
+ if (r == -ENOTSUP) {
+ log_dbg(cd, "Kernel keyring features disabled.");
+ return -EINVAL;
+ } else if (r < 0) {
+ log_dbg(cd, "keyring_get_passphrase failed (error %d)", r);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int keyring_validate(struct crypt_device *cd __attribute__((unused)),
+ const char *json)
+{
+ enum json_tokener_error jerr;
+ json_object *jobj_token, *jobj_key;
+ int r = 1;
+
+ log_dbg(cd, "Validating keyring token json");
+
+ jobj_token = json_tokener_parse_verbose(json, &jerr);
+ if (!jobj_token) {
+ log_dbg(cd, "Keyring token JSON parse failed.");
+ return r;
+ }
+
+ if (json_object_object_length(jobj_token) != 3) {
+ log_dbg(cd, "Keyring token is expected to have exactly 3 fields.");
+ goto out;
+ }
+
+ if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
+ log_dbg(cd, "missing key_description field.");
+ goto out;
+ }
+
+ if (!json_object_is_type(jobj_key, json_type_string)) {
+ log_dbg(cd, "key_description is not a string.");
+ goto out;
+ }
+
+ /* TODO: perhaps check that key description is in '%s:%s'
+ * format where both strings are not empty */
+ r = !strlen(json_object_get_string(jobj_key));
+out:
+ json_object_put(jobj_token);
+ return r;
+}
+
+static void keyring_dump(struct crypt_device *cd, const char *json)
+{
+ enum json_tokener_error jerr;
+ json_object *jobj_token, *jobj_key;
+
+ jobj_token = json_tokener_parse_verbose(json, &jerr);
+ if (!jobj_token)
+ return;
+
+ if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
+ json_object_put(jobj_token);
+ return;
+ }
+
+ log_std(cd, "\tKey description: %s\n", json_object_get_string(jobj_key));
+
+ json_object_put(jobj_token);
+}
+
+int token_keyring_set(json_object **jobj_builtin_token,
+ const void *params)
+{
+ json_object *jobj_token, *jobj;
+ const struct crypt_token_params_luks2_keyring *keyring_params = (const struct crypt_token_params_luks2_keyring *) params;
+
+ jobj_token = json_object_new_object();
+ if (!jobj_token)
+ return -ENOMEM;
+
+ jobj = json_object_new_string(LUKS2_TOKEN_KEYRING);
+ if (!jobj) {
+ json_object_put(jobj_token);
+ return -ENOMEM;
+ }
+ json_object_object_add(jobj_token, "type", jobj);
+
+ jobj = json_object_new_array();
+ if (!jobj) {
+ json_object_put(jobj_token);
+ return -ENOMEM;
+ }
+ json_object_object_add(jobj_token, "keyslots", jobj);
+
+ jobj = json_object_new_string(keyring_params->key_description);
+ if (!jobj) {
+ json_object_put(jobj_token);
+ return -ENOMEM;
+ }
+ json_object_object_add(jobj_token, "key_description", jobj);
+
+ *jobj_builtin_token = jobj_token;
+ return 0;
+}
+
+int token_keyring_get(json_object *jobj_token,
+ void *params)
+{
+ json_object *jobj;
+ struct crypt_token_params_luks2_keyring *keyring_params = (struct crypt_token_params_luks2_keyring *) params;
+
+ json_object_object_get_ex(jobj_token, "type", &jobj);
+ assert(!strcmp(json_object_get_string(jobj), LUKS2_TOKEN_KEYRING));
+
+ json_object_object_get_ex(jobj_token, "key_description", &jobj);
+
+ keyring_params->key_description = json_object_get_string(jobj);
+
+ return 0;
+}
+
+const crypt_token_handler keyring_handler = {
+ .name = LUKS2_TOKEN_KEYRING,
+ .open = keyring_open,
+ .validate = keyring_validate,
+ .dump = keyring_dump
+};