X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fluks2%2Fluks2_keyslot_reenc.c;h=4291d0cb01eca70f63f5cdeb7407aa7fb75c9db5;hb=6497abd1df88001eb1f45f7348534911b33d05b5;hp=b939467f67494dcfd5f87b4795fd88dcbd3b455b;hpb=322b430a2589cdc7985e98a14ec12322b91c9d5e;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c index b939467..4291d0c 100644 --- a/lib/luks2/luks2_keyslot_reenc.c +++ b/lib/luks2/luks2_keyslot_reenc.c @@ -1,8 +1,8 @@ /* * LUKS - Linux Unified Key Setup v2, reencryption keyslot handler * - * Copyright (C) 2016-2020, Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2020, Ondrej Kozina + * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2023 Ondrej Kozina * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,20 +21,87 @@ #include "luks2_internal.h" -static int reenc_keyslot_open(struct crypt_device *cd, - int keyslot, - const char *password, - size_t password_len, - char *volume_key, - size_t volume_key_len) +static int reenc_keyslot_open(struct crypt_device *cd __attribute__((unused)), + int keyslot __attribute__((unused)), + const char *password __attribute__((unused)), + size_t password_len __attribute__((unused)), + char *volume_key __attribute__((unused)), + size_t volume_key_len __attribute__((unused))) { return -ENOENT; } -int reenc_keyslot_alloc(struct crypt_device *cd, +static json_object *reencrypt_keyslot_area_jobj(struct crypt_device *cd, + const struct crypt_params_reencrypt *params, + size_t alignment, + uint64_t area_offset, + uint64_t area_length) +{ + json_object *jobj_area = json_object_new_object(); + + if (!jobj_area || !params || !params->resilience) + return NULL; + + json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset)); + json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length)); + json_object_object_add(jobj_area, "type", json_object_new_string(params->resilience)); + + if (!strcmp(params->resilience, "checksum")) { + log_dbg(cd, "Setting reencrypt keyslot for checksum protection."); + json_object_object_add(jobj_area, "hash", json_object_new_string(params->hash)); + json_object_object_add(jobj_area, "sector_size", json_object_new_int64(alignment)); + } else if (!strcmp(params->resilience, "journal")) { + log_dbg(cd, "Setting reencrypt keyslot for journal protection."); + } else if (!strcmp(params->resilience, "none")) { + log_dbg(cd, "Setting reencrypt keyslot for none protection."); + } else if (!strcmp(params->resilience, "datashift")) { + log_dbg(cd, "Setting reencrypt keyslot for datashift protection."); + json_object_object_add(jobj_area, "shift_size", + crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); + } else if (!strcmp(params->resilience, "datashift-checksum")) { + log_dbg(cd, "Setting reencrypt keyslot for datashift and checksum protection."); + json_object_object_add(jobj_area, "hash", json_object_new_string(params->hash)); + json_object_object_add(jobj_area, "sector_size", json_object_new_int64(alignment)); + json_object_object_add(jobj_area, "shift_size", + crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); + } else if (!strcmp(params->resilience, "datashift-journal")) { + log_dbg(cd, "Setting reencrypt keyslot for datashift and journal protection."); + json_object_object_add(jobj_area, "shift_size", + crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); + } else { + json_object_put(jobj_area); + return NULL; + } + + return jobj_area; +} + +static json_object *reencrypt_keyslot_area_jobj_update_block_size(struct crypt_device *cd, + json_object *jobj_area, size_t alignment) +{ + json_object *jobj_type, *jobj_area_new = NULL; + + if (!jobj_area || + !json_object_object_get_ex(jobj_area, "type", &jobj_type) || + (strcmp(json_object_get_string(jobj_type), "checksum") && + strcmp(json_object_get_string(jobj_type), "datashift-checksum"))) + return NULL; + + if (json_object_copy(jobj_area, &jobj_area_new)) + return NULL; + + log_dbg(cd, "Updating reencrypt resilience checksum block size."); + + json_object_object_add(jobj_area_new, "sector_size", json_object_new_int64(alignment)); + + return jobj_area_new; +} + +static int reenc_keyslot_alloc(struct crypt_device *cd, struct luks2_hdr *hdr, int keyslot, - const struct crypt_params_reencrypt *params) + const struct crypt_params_reencrypt *params, + size_t alignment) { int r; json_object *jobj_keyslots, *jobj_keyslot, *jobj_area; @@ -42,50 +109,41 @@ int reenc_keyslot_alloc(struct crypt_device *cd, log_dbg(cd, "Allocating reencrypt keyslot %d.", keyslot); + if (!params || !params->resilience || params->direction > CRYPT_REENCRYPT_BACKWARD) + return -EINVAL; + if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX) return -ENOMEM; if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots)) return -EINVAL; - /* encryption doesn't require area (we shift data and backup will be available) */ - if (!params->data_shift) { - r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length); - if (r < 0) - return r; - } else { /* we can't have keyslot w/o area...bug? */ + /* only plain datashift resilience mode does not require additional storage */ + if (!strcmp(params->resilience, "datashift")) r = LUKS2_find_area_gap(cd, hdr, 1, &area_offset, &area_length); - if (r < 0) - return r; - } + else + r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length); + if (r < 0) + return r; + + jobj_area = reencrypt_keyslot_area_jobj(cd, params, alignment, area_offset, area_length); + if (!jobj_area) + return -EINVAL; jobj_keyslot = json_object_new_object(); - if (!jobj_keyslot) + if (!jobj_keyslot) { + json_object_put(jobj_area); return -ENOMEM; - - jobj_area = json_object_new_object(); - - if (params->data_shift) { - json_object_object_add(jobj_area, "type", json_object_new_string("datashift")); - json_object_object_add(jobj_area, "shift_size", crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT)); - } else - /* except data shift protection, initial setting is irrelevant. Type can be changed during reencryption */ - json_object_object_add(jobj_area, "type", json_object_new_string("none")); - - json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset)); - json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length)); + } + json_object_object_add(jobj_keyslot, "area", jobj_area); json_object_object_add(jobj_keyslot, "type", json_object_new_string("reencrypt")); json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(1)); /* useless but mandatory */ json_object_object_add(jobj_keyslot, "mode", json_object_new_string(crypt_reencrypt_mode_to_str(params->mode))); if (params->direction == CRYPT_REENCRYPT_FORWARD) json_object_object_add(jobj_keyslot, "direction", json_object_new_string("forward")); - else if (params->direction == CRYPT_REENCRYPT_BACKWARD) - json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward")); else - return -EINVAL; - - json_object_object_add(jobj_keyslot, "area", jobj_area); + json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward")); json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot); if (LUKS2_check_json_size(cd, hdr)) { @@ -176,43 +234,17 @@ static int reenc_keyslot_store(struct crypt_device *cd, return r < 0 ? r : keyslot; } -int reenc_keyslot_update(struct crypt_device *cd, - const struct luks2_reenc_context *rh) +static int reenc_keyslot_wipe(struct crypt_device *cd, + int keyslot) { - json_object *jobj_keyslot, *jobj_area, *jobj_area_type; struct luks2_hdr *hdr; if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) return -EINVAL; - jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, rh->reenc_keyslot); - if (!jobj_keyslot) - return -EINVAL; - - json_object_object_get_ex(jobj_keyslot, "area", &jobj_area); - json_object_object_get_ex(jobj_area, "type", &jobj_area_type); - - if (rh->rp.type == REENC_PROTECTION_CHECKSUM) { - log_dbg(cd, "Updating reencrypt keyslot for checksum protection."); - json_object_object_add(jobj_area, "type", json_object_new_string("checksum")); - json_object_object_add(jobj_area, "hash", json_object_new_string(rh->rp.p.csum.hash)); - json_object_object_add(jobj_area, "sector_size", json_object_new_int64(rh->alignment)); - } else if (rh->rp.type == REENC_PROTECTION_NONE) { - log_dbg(cd, "Updating reencrypt keyslot for none protection."); - json_object_object_add(jobj_area, "type", json_object_new_string("none")); - json_object_object_del(jobj_area, "hash"); - } else if (rh->rp.type == REENC_PROTECTION_JOURNAL) { - log_dbg(cd, "Updating reencrypt keyslot for journal protection."); - json_object_object_add(jobj_area, "type", json_object_new_string("journal")); - json_object_object_del(jobj_area, "hash"); - } else - log_dbg(cd, "No update of reencrypt keyslot needed."); - - return 0; -} + /* remove reencryption verification data */ + LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0); -static int reenc_keyslot_wipe(struct crypt_device *cd, int keyslot) -{ return 0; } @@ -256,7 +288,8 @@ static int reenc_keyslot_dump(struct crypt_device *cd, int keyslot) static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot) { - json_object *jobj_mode, *jobj_area, *jobj_type, *jobj_shift_size, *jobj_hash, *jobj_sector_size, *jobj_direction; + json_object *jobj_mode, *jobj_area, *jobj_type, *jobj_shift_size, *jobj_hash, + *jobj_sector_size, *jobj_direction, *jobj_key_size; const char *mode, *type, *direction; uint32_t sector_size; uint64_t shift_size; @@ -264,10 +297,10 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key /* mode (string: encrypt,reencrypt,decrypt) * direction (string:) * area { - * type: (string: datashift, journal, checksum, none) - * hash: (string: checksum only) - * sector_size (uint32: checksum only) - * shift_size (uint64: datashift only) + * type: (string: datashift, journal, checksum, none, datashift-journal, datashift-checksum) + * hash: (string: checksum and datashift-checksum types) + * sector_size (uint32: checksum and datashift-checksum types) + * shift_size (uint64: all datashift based types) * } */ @@ -276,12 +309,18 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key !json_object_object_get_ex(jobj_area, "type", &jobj_type)) return -EINVAL; - jobj_mode = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "mode", json_type_string); - jobj_direction = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "direction", json_type_string); + jobj_key_size = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "key_size", json_type_int); + jobj_mode = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "mode"); + jobj_direction = json_contains_string(cd, jobj_keyslot, "", "reencrypt keyslot", "direction"); - if (!jobj_mode || !jobj_direction) + if (!jobj_mode || !jobj_direction || !jobj_key_size) return -EINVAL; + if (!validate_json_uint32(jobj_key_size) || crypt_jobj_get_uint32(jobj_key_size) != 1) { + log_dbg(cd, "Illegal reencrypt key size."); + return -EINVAL; + } + mode = json_object_get_string(jobj_mode); type = json_object_get_string(jobj_type); direction = json_object_get_string(jobj_direction); @@ -297,20 +336,26 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key return -EINVAL; } - if (!strcmp(type, "checksum")) { - jobj_hash = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "hash", json_type_string); - jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "sector_size", json_type_int); + if (!strcmp(type, "checksum") || !strcmp(type, "datashift-checksum")) { + jobj_hash = json_contains_string(cd, jobj_area, "type:checksum", + "Keyslot area", "hash"); + jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", + "Keyslot area", "sector_size", json_type_int); if (!jobj_hash || !jobj_sector_size) return -EINVAL; if (!validate_json_uint32(jobj_sector_size)) return -EINVAL; sector_size = crypt_jobj_get_uint32(jobj_sector_size); if (sector_size < SECTOR_SIZE || NOTPOW2(sector_size)) { - log_dbg(cd, "Invalid sector_size (%" PRIu32 ") for checksum resilience mode.", sector_size); + log_dbg(cd, "Invalid sector_size (%" PRIu32 ") for checksum resilience mode.", + sector_size); return -EINVAL; } - } else if (!strcmp(type, "datashift")) { - if (!(jobj_shift_size = json_contains(cd, jobj_area, "type:datashift", "Keyslot area", "shift_size", json_type_string))) + } else if (!strcmp(type, "datashift") || + !strcmp(type, "datashift-checksum") || + !strcmp(type, "datashift-journal")) { + if (!(jobj_shift_size = json_contains_string(cd, jobj_area, "type:datashift", + "Keyslot area", "shift_size"))) return -EINVAL; shift_size = crypt_jobj_get_uint64(jobj_shift_size); @@ -318,14 +363,385 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key return -EINVAL; if (MISALIGNED_512(shift_size)) { - log_dbg(cd, "Shift size field has to be aligned to sector size: %" PRIu32, SECTOR_SIZE); + 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.")); return -EINVAL; } + + jobj_area_new = reencrypt_keyslot_area_jobj(cd, params, alignment, + area_offset, area_length); + } + + if (!jobj_area_new) + return -EINVAL; + + /* increase refcount for validation purposes */ + json_object_get(jobj_area); + + json_object_object_add(jobj_keyslot, "area", jobj_area_new); + + r = reenc_keyslot_validate(cd, jobj_keyslot); + if (r) { + /* replace invalid object with previous valid one */ + json_object_object_add(jobj_keyslot, "area", jobj_area); + return -EINVAL; + } + + /* previous area object is no longer needed */ + json_object_put(jobj_area); + + return 0; +} + +int LUKS2_keyslot_reencrypt_allocate(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot, + const struct crypt_params_reencrypt *params, + size_t alignment) +{ + int r; + + if (keyslot == CRYPT_ANY_SLOT) + return -EINVAL; + + r = reenc_keyslot_alloc(cd, hdr, keyslot, params, alignment); + if (r < 0) + return r; + + r = LUKS2_keyslot_priority_set(cd, hdr, keyslot, CRYPT_SLOT_PRIORITY_IGNORE, 0); + if (r < 0) + return r; + + r = reenc_keyslot_validate(cd, LUKS2_get_keyslot_jobj(hdr, keyslot)); + if (r) { + log_dbg(cd, "Keyslot validation failed."); + return r; } return 0; } +int LUKS2_keyslot_reencrypt_update_needed(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot, + const struct crypt_params_reencrypt *params, + size_t alignment) +{ + int r; + json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); + + if (!jobj_keyslot || + !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || + strcmp(json_object_get_string(jobj_type), "reencrypt")) + return -EINVAL; + + r = reenc_keyslot_update_needed(cd, jobj_keyslot, params, alignment); + if (!r) + log_dbg(cd, "No update of reencrypt keyslot needed."); + + return r; +} + +int LUKS2_keyslot_reencrypt_update(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot, + const struct crypt_params_reencrypt *params, + size_t alignment, + struct volume_key *vks) +{ + int r; + uint8_t version; + uint64_t max_size, moved_segment_size; + json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); + struct reenc_protection check_rp = {}; + + if (!jobj_keyslot || + !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || + strcmp(json_object_get_string(jobj_type), "reencrypt")) + return -EINVAL; + + if (LUKS2_config_get_reencrypt_version(hdr, &version)) + return -EINVAL; + + /* verify existing reencryption metadata before updating */ + r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); + if (r < 0) + return r; + + r = reenc_keyslot_update(cd, jobj_keyslot, params, alignment); + if (r < 0) + return r; + + r = reenc_keyslot_load_resilience(cd, jobj_keyslot, &check_rp, false); + if (r < 0) + return r; + + if (check_rp.type != REENC_PROTECTION_NOT_SET) { + r = LUKS2_reencrypt_max_hotzone_size(cd, hdr, &check_rp, keyslot, &max_size); + LUKS2_reencrypt_protection_erase(&check_rp); + if (r < 0) + return r; + moved_segment_size = json_segment_get_size(LUKS2_get_segment_by_flag(hdr, "backup-moved-segment"), 0); + if (!moved_segment_size) + return -EINVAL; + if (moved_segment_size > max_size) { + log_err(cd, _("Can not update resilience type. " + "New type only provides %" PRIu64 " bytes, " + "required space is: %" PRIu64 " bytes."), + max_size, moved_segment_size); + return -EINVAL; + } + } + + r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, version, vks); + if (r < 0) + log_err(cd, _("Failed to refresh reencryption verification digest.")); + + return r ?: LUKS2_hdr_write(cd, hdr); +} + +int LUKS2_keyslot_reencrypt_load(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot, + struct reenc_protection *rp, + bool primary) +{ + json_object *jobj_type, *jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); + + if (!jobj_keyslot || + !json_object_object_get_ex(jobj_keyslot, "type", &jobj_type) || + strcmp(json_object_get_string(jobj_type), "reencrypt")) + return -EINVAL; + + return reenc_keyslot_load_resilience(cd, jobj_keyslot, rp, primary); +} + const keyslot_handler reenc_keyslot = { .name = "reencrypt", .open = reenc_keyslot_open,