+ log_err(_("Activation of temporary devices failed."));
+ return r;
+}
+
+static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type)
+{
+ const struct crypt_pbkdf_type *pbkdf_default;
+ struct crypt_pbkdf_type pbkdf = {};
+
+ pbkdf_default = crypt_get_pbkdf_default(dev_type);
+ if (!pbkdf_default)
+ return -EINVAL;
+
+ pbkdf.type = opt_pbkdf ?: pbkdf_default->type;
+ pbkdf.hash = opt_hash ?: pbkdf_default->hash;
+ pbkdf.time_ms = (uint32_t)opt_iteration_time ?: pbkdf_default->time_ms;
+ if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) {
+ pbkdf.max_memory_kb = (uint32_t)opt_pbkdf_memory ?: pbkdf_default->max_memory_kb;
+ pbkdf.parallel_threads = (uint32_t)opt_pbkdf_parallel ?: pbkdf_default->parallel_threads;
+ }
+
+ if (opt_pbkdf_iterations) {
+ pbkdf.iterations = opt_pbkdf_iterations;
+ pbkdf.time_ms = 0;
+ pbkdf.flags |= CRYPT_PBKDF_NO_BENCHMARK;
+ }
+
+ return crypt_set_pbkdf_type(cd, &pbkdf);
+}
+
+static int create_new_keyslot(struct reenc_ctx *rc, int keyslot,
+ struct crypt_device *cd_old,
+ struct crypt_device *cd_new)
+{
+ int r;
+ char *key = NULL;
+ size_t key_size;
+
+ if (cd_old && crypt_keyslot_status(cd_old, keyslot) == CRYPT_SLOT_UNBOUND) {
+ key_size = 4096;
+ key = crypt_safe_alloc(key_size);
+ if (!key)
+ return -ENOMEM;
+ r = crypt_volume_key_get(cd_old, keyslot, key, &key_size,
+ rc->p[keyslot].password, rc->p[keyslot].passwordLen);
+ if (r == keyslot) {
+ r = crypt_keyslot_add_by_key(cd_new, keyslot, key, key_size,
+ rc->p[keyslot].password, rc->p[keyslot].passwordLen,
+ CRYPT_VOLUME_KEY_NO_SEGMENT);
+ } else
+ r = -EINVAL;
+ crypt_safe_free(key);
+ } else
+ r = crypt_keyslot_add_by_volume_key(cd_new, keyslot, NULL, 0,
+ rc->p[keyslot].password, rc->p[keyslot].passwordLen);
+
+ return r;
+}
+
+static int create_new_header(struct reenc_ctx *rc, struct crypt_device *cd_old,
+ const char *cipher, const char *cipher_mode,
+ const char *uuid,
+ const char *key, int key_size,
+ const char *type,
+ uint64_t metadata_size,
+ uint64_t keyslots_size,
+ void *params)
+{
+ struct crypt_device *cd_new = NULL;
+ int i, r;
+
+ if ((r = crypt_init(&cd_new, rc->header_file_new)))
+ goto out;
+
+ if (opt_random)
+ crypt_set_rng_type(cd_new, CRYPT_RNG_RANDOM);
+ else if (opt_urandom)
+ crypt_set_rng_type(cd_new, CRYPT_RNG_URANDOM);
+
+ r = set_pbkdf_params(cd_new, type);
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ r = crypt_set_data_offset(cd_new, rc->data_offset);
+ if (r) {
+ log_err(_("Failed to set data offset."));
+ goto out;
+ }
+
+ r = crypt_set_metadata_size(cd_new, metadata_size, keyslots_size);
+ if (r) {
+ log_err(_("Failed to set metadata size."));
+ goto out;
+ }
+
+ r = crypt_format(cd_new, type, cipher, cipher_mode, uuid, key, key_size, params);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ log_verbose(_("New LUKS header for device %s created."), rc->device);
+
+ for (i = 0; i < crypt_keyslot_max(type); i++) {
+ if (!rc->p[i].password)
+ continue;
+
+ r = create_new_keyslot(rc, i, cd_old, cd_new);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, CREATED);
+ r = 0;
+ }
+out:
+ crypt_free(cd_new);
+ return r;
+}
+
+static int isLUKS2(const char *type)
+{
+ return (type && !strcmp(type, CRYPT_LUKS2));
+}
+
+static int luks2_metadata_copy(struct reenc_ctx *rc)
+{
+ const char *json, *type;
+ crypt_token_info ti;
+ uint32_t flags;
+ int i, r = -EINVAL;
+ struct crypt_device *cd_old = NULL, *cd_new = NULL;
+
+ if (crypt_init(&cd_old, rc->header_file_tmp) ||
+ crypt_load(cd_old, CRYPT_LUKS2, NULL))
+ goto out;
+
+ if (crypt_init(&cd_new, rc->header_file_new) ||
+ crypt_load(cd_new, CRYPT_LUKS2, NULL))
+ goto out;
+
+ /*
+ * we have to erase keyslots missing in new header so that we can
+ * transfer tokens from old header to new one
+ */
+ for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS2); i++)
+ if (!rc->p[i].password && crypt_keyslot_status(cd_old, i) == CRYPT_SLOT_ACTIVE) {
+ r = crypt_keyslot_destroy(cd_old, i);
+ if (r < 0)
+ goto out;
+ }
+
+ for (i = 0; i < MAX_TOKEN; i++) {
+ ti = crypt_token_status(cd_old, i, &type);
+ switch (ti) {
+ case CRYPT_TOKEN_INVALID:
+ log_dbg("Internal error.");
+ r = -EINVAL;
+ goto out;
+ case CRYPT_TOKEN_INACTIVE:
+ break;
+ case CRYPT_TOKEN_INTERNAL_UNKNOWN:
+ log_err(_("This version of cryptsetup-reencrypt can't handle new internal token type %s."), type);
+ r = -EINVAL;
+ goto out;
+ case CRYPT_TOKEN_INTERNAL:
+ /* fallthrough */
+ case CRYPT_TOKEN_EXTERNAL:
+ /* fallthrough */
+ case CRYPT_TOKEN_EXTERNAL_UNKNOWN:
+ if (crypt_token_json_get(cd_old, i, &json) != i) {
+ log_dbg("Failed to get %s token (%d).", type, i);
+ r = -EINVAL;
+ goto out;
+ }
+ if (crypt_token_json_set(cd_new, i, json) != i) {
+ log_dbg("Failed to create %s token (%d).", type, i);
+ r = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ if ((r = crypt_persistent_flags_get(cd_old, CRYPT_FLAGS_ACTIVATION, &flags))) {
+ log_err(_("Failed to read activation flags from backup header."));
+ goto out;
+ }
+ if ((r = crypt_persistent_flags_set(cd_new, CRYPT_FLAGS_ACTIVATION, flags))) {
+ log_err(_("Failed to write activation flags to new header."));
+ goto out;
+ }
+ if ((r = crypt_persistent_flags_get(cd_old, CRYPT_FLAGS_REQUIREMENTS, &flags))) {
+ log_err(_("Failed to read requirements from backup header."));
+ goto out;
+ }
+ if ((r = crypt_persistent_flags_set(cd_new, CRYPT_FLAGS_REQUIREMENTS, flags)))
+ log_err(_("Failed to read requirements from backup header."));
+out:
+ crypt_free(cd_old);
+ crypt_free(cd_new);
+ unlink(rc->header_file_tmp);
+