+ log_dbg(cd, "Shift size field has to be aligned to 512 bytes.");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int reenc_keyslot_update_needed(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ const struct crypt_params_reencrypt *params,
+ size_t alignment)
+{
+ const char *type;
+ json_object *jobj_area, *jobj_type, *jobj;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
+ !json_object_object_get_ex(jobj_area, "type", &jobj_type) ||
+ !(type = json_object_get_string(jobj_type)))
+ return -EINVAL;
+
+ /*
+ * If no resilience mode change is requested and effective
+ * resilience mode is 'checksum' then check alignment matches
+ * stored checksum block size.
+ */
+ if (!params || !params->resilience) {
+ if (!strcmp(json_object_get_string(jobj_type), "checksum") ||
+ !strcmp(json_object_get_string(jobj_type), "datashift-checksum"))
+ return (json_object_object_get_ex(jobj_area, "sector_size", &jobj) ||
+ alignment != crypt_jobj_get_uint32(jobj));
+ return 0;
+ }
+
+ if (strcmp(params->resilience, type))
+ return 1;
+
+ if (!strcmp(type, "checksum") ||
+ !strcmp(type, "datashift-checksum")) {
+ if (!params->hash)
+ return -EINVAL;
+ if (!json_object_object_get_ex(jobj_area, "hash", &jobj) ||
+ strcmp(json_object_get_string(jobj), params->hash) ||
+ !json_object_object_get_ex(jobj_area, "sector_size", &jobj) ||
+ crypt_jobj_get_uint32(jobj) != alignment)
+ return 1;
+ }
+
+ if (!strncmp(type, "datashift", 9)) {
+ if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj))
+ return -EINVAL;
+ if ((params->data_shift << SECTOR_SHIFT) != crypt_jobj_get_uint64(jobj))
+ return 1;
+ }
+
+ /* nothing to compare with 'none' and 'journal' */
+ return 0;
+}
+
+static int load_checksum_protection(struct crypt_device *cd,
+ json_object *jobj_area,
+ uint64_t area_length,
+ struct reenc_protection *rp)
+{
+ int r;
+ json_object *jobj_hash, *jobj_block_size;
+
+ if (!jobj_area || !rp ||
+ !json_object_object_get_ex(jobj_area, "hash", &jobj_hash) ||
+ !json_object_object_get_ex(jobj_area, "sector_size", &jobj_block_size))
+ return -EINVAL;
+
+ r = snprintf(rp->p.csum.hash, sizeof(rp->p.csum.hash), "%s", json_object_get_string(jobj_hash));
+ if (r < 0 || (size_t)r >= sizeof(rp->p.csum.hash))
+ return -EINVAL;
+
+ if (crypt_hash_init(&rp->p.csum.ch, rp->p.csum.hash)) {
+ log_err(cd, _("Hash algorithm %s is not available."), rp->p.csum.hash);
+ return -EINVAL;
+ }
+
+ r = crypt_hash_size(rp->p.csum.hash);
+ if (r <= 0) {
+ crypt_hash_destroy(rp->p.csum.ch);
+ rp->p.csum.ch = NULL;
+ log_dbg(cd, "Invalid hash size");
+ return -EINVAL;
+ }
+
+ rp->p.csum.hash_size = r;
+ rp->p.csum.block_size = crypt_jobj_get_uint32(jobj_block_size);
+ rp->p.csum.checksums_len = area_length;
+
+ rp->type = REENC_PROTECTION_CHECKSUM;
+ return 0;
+}
+
+static int reenc_keyslot_load_resilience_primary(struct crypt_device *cd,
+ const char *type,
+ json_object *jobj_area,
+ uint64_t area_length,
+ struct reenc_protection *rp)
+{
+ json_object *jobj;
+
+ if (!strcmp(type, "checksum")) {
+ log_dbg(cd, "Initializing checksum resilience mode.");
+ return load_checksum_protection(cd, jobj_area, area_length, rp);
+ } else if (!strcmp(type, "journal")) {
+ log_dbg(cd, "Initializing journal resilience mode.");
+ rp->type = REENC_PROTECTION_JOURNAL;
+ } else if (!strcmp(type, "none")) {
+ log_dbg(cd, "Initializing none resilience mode.");
+ rp->type = REENC_PROTECTION_NONE;
+ } else if (!strcmp(type, "datashift") ||
+ !strcmp(type, "datashift-checksum") ||
+ !strcmp(type, "datashift-journal")) {
+ log_dbg(cd, "Initializing datashift resilience mode.");
+ if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj))
+ return -EINVAL;
+ rp->type = REENC_PROTECTION_DATASHIFT;
+ rp->p.ds.data_shift = crypt_jobj_get_uint64(jobj);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int reenc_keyslot_load_resilience_secondary(struct crypt_device *cd,
+ const char *type,
+ json_object *jobj_area,
+ uint64_t area_length,
+ struct reenc_protection *rp)
+{
+ if (!strcmp(type, "datashift-checksum")) {
+ log_dbg(cd, "Initializing checksum resilience mode.");
+ return load_checksum_protection(cd, jobj_area, area_length, rp);
+ } else if (!strcmp(type, "datashift-journal")) {
+ log_dbg(cd, "Initializing journal resilience mode.");
+ rp->type = REENC_PROTECTION_JOURNAL;
+ } else
+ rp->type = REENC_PROTECTION_NOT_SET;
+
+ return 0;
+}
+
+static int reenc_keyslot_load_resilience(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ struct reenc_protection *rp,
+ bool primary)
+{
+ const char *type;
+ int r;
+ json_object *jobj_area, *jobj_type;
+ uint64_t dummy, area_length;
+
+ if (!rp || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
+ !json_object_object_get_ex(jobj_area, "type", &jobj_type))
+ return -EINVAL;
+
+ r = LUKS2_keyslot_jobj_area(jobj_keyslot, &dummy, &area_length);
+ if (r < 0)
+ return r;
+
+ type = json_object_get_string(jobj_type);
+ if (!type)
+ return -EINVAL;
+
+ if (primary)
+ return reenc_keyslot_load_resilience_primary(cd, type, jobj_area, area_length, rp);
+ else
+ return reenc_keyslot_load_resilience_secondary(cd, type, jobj_area, area_length, rp);
+}
+
+static bool reenc_keyslot_update_is_valid(struct crypt_device *cd,
+ json_object *jobj_area,
+ const struct crypt_params_reencrypt *params)
+{
+ const char *type;
+ json_object *jobj_type, *jobj;
+
+ if (!json_object_object_get_ex(jobj_area, "type", &jobj_type) ||
+ !(type = json_object_get_string(jobj_type)))
+ return false;
+
+ /* do not allow switch to/away from datashift resilience type */
+ if ((strcmp(params->resilience, "datashift") && !strcmp(type, "datashift")) ||
+ (!strcmp(params->resilience, "datashift") && strcmp(type, "datashift")))
+ return false;
+
+ /* do not allow switch to/away from datashift- resilience subvariants */
+ if ((strncmp(params->resilience, "datashift-", 10) &&
+ !strncmp(type, "datashift-", 10)) ||
+ (!strncmp(params->resilience, "datashift-", 10) &&
+ strncmp(type, "datashift-", 10)))
+ return false;
+
+ /* datashift value is also immutable */
+ if (!strncmp(type, "datashift", 9)) {
+ if (!json_object_object_get_ex(jobj_area, "shift_size", &jobj))
+ return false;
+ return (params->data_shift << SECTOR_SHIFT) == crypt_jobj_get_uint64(jobj);
+ }
+
+ return true;
+}
+
+static int reenc_keyslot_update(struct crypt_device *cd,
+ json_object *jobj_keyslot,
+ const struct crypt_params_reencrypt *params,
+ size_t alignment)
+{
+ int r;
+ json_object *jobj_area, *jobj_area_new;
+ uint64_t area_offset, area_length;
+
+ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
+ return -EINVAL;
+
+ r = LUKS2_keyslot_jobj_area(jobj_keyslot, &area_offset, &area_length);
+ if (r < 0)
+ return r;
+
+ if (!params || !params->resilience)
+ jobj_area_new = reencrypt_keyslot_area_jobj_update_block_size(cd, jobj_area, alignment);
+ else {
+ if (!reenc_keyslot_update_is_valid(cd, jobj_area, params)) {
+ log_err(cd, _("Invalid reencryption resilience mode change requested."));