From fc4b2cab2591f9fd125d9705191df29040f1fccd Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Mon, 16 May 2022 16:40:18 +0200 Subject: [PATCH] Store proper resilience data in LUKS2 reencrypt initialization. Prior to commit 0113ac2d889c5322659ad0596d4cfc6da53e356c it did not matter what resilince metadata we stored during initialization. So we stored 'none' type unless 'datashift' operation was initialized. After the commit, it triggered reencryption metadata digest refresh almost each time (except 'datashift') which was suboptimal. By storing proper resilience type during reencryption initialization we will avoid the needless reencryption digest refresh later (after update optimization). --- lib/luks2/luks2_internal.h | 3 +- lib/luks2/luks2_keyslot_reenc.c | 68 +++++++++++++++++++++++---------- lib/luks2/luks2_reencrypt.c | 49 +++++++++++++++++++++--- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h index f2fba58f..1b96ecbb 100644 --- a/lib/luks2/luks2_internal.h +++ b/lib/luks2/luks2_internal.h @@ -251,7 +251,8 @@ int LUKS2_keyslot_reencrypt_store(struct crypt_device *cd, int LUKS2_keyslot_reencrypt_allocate(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 LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, struct luks2_hdr *hdr, diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c index 56944dea..32af7d4b 100644 --- a/lib/luks2/luks2_keyslot_reenc.c +++ b/lib/luks2/luks2_keyslot_reenc.c @@ -31,10 +31,45 @@ static int reenc_keyslot_open(struct crypt_device *cd __attribute__((unused)), return -ENOENT; } +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 { + json_object_put(jobj_area); + return NULL; + } + + return jobj_area; +} + 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,7 +77,7 @@ static int reenc_keyslot_alloc(struct crypt_device *cd, log_dbg(cd, "Allocating reencrypt keyslot %d.", keyslot); - if (!params || params->direction > CRYPT_REENCRYPT_BACKWARD) + if (!params || !params->resilience || params->direction > CRYPT_REENCRYPT_BACKWARD) return -EINVAL; if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX) @@ -52,7 +87,7 @@ static int reenc_keyslot_alloc(struct crypt_device *cd, return -EINVAL; /* encryption doesn't require area (we shift data and backup will be available) */ - if (!params->data_shift) { + if (strcmp(params->resilience, "datashift")) { r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length); if (r < 0) return r; @@ -62,27 +97,17 @@ static int reenc_keyslot_alloc(struct crypt_device *cd, return r; } - jobj_keyslot = json_object_new_object(); - if (!jobj_keyslot) - return -ENOMEM; + jobj_area = reencrypt_keyslot_area_jobj(cd, params, alignment, area_offset, area_length); + if (!jobj_area) + return -EINVAL; - jobj_area = json_object_new_object(); - if (!jobj_area) { - json_object_put(jobj_keyslot); + jobj_keyslot = json_object_new_object(); + if (!jobj_keyslot) { + json_object_put(jobj_area); return -ENOMEM; } json_object_object_add(jobj_keyslot, "area", jobj_area); - 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, "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))); @@ -313,14 +338,15 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key int LUKS2_keyslot_reencrypt_allocate(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; if (keyslot == CRYPT_ANY_SLOT) return -EINVAL; - r = reenc_keyslot_alloc(cd, hdr, keyslot, params); + r = reenc_keyslot_alloc(cd, hdr, keyslot, params, alignment); if (r < 0) return r; diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c index 1542e8e0..ecbace5d 100644 --- a/lib/luks2/luks2_reencrypt.c +++ b/lib/luks2/luks2_reencrypt.c @@ -2393,6 +2393,41 @@ static int reencrypt_verify_and_upload_keys(struct crypt_device *cd, return 0; } +static int reencrypt_verify_resilience_params(struct crypt_device *cd, + const struct crypt_params_reencrypt *params) +{ + struct crypt_hash *ch; + + if (!params || !params->resilience) + return 0; + + if (!strcmp(params->resilience, "journal")) + return 0; + else if (!strcmp(params->resilience, "none")) + return 0; + else if (!strcmp(params->resilience, "datashift")) { + return params->data_shift ? 0 : -EINVAL; + } else if (!strcmp(params->resilience, "checksum")) { + if (!params->hash || strlen(params->hash) > (LUKS2_CHECKSUM_ALG_L - 1)) + return -EINVAL; + + if (crypt_hash_size(params->hash) <= 0) + return -EINVAL; + + if (crypt_hash_init(&ch, params->hash)) { + log_err(cd, _("Hash algorithm %s is not available."), params->hash); + return -EINVAL; + } + /* We just check for alg availability */ + crypt_hash_destroy(ch); + } else { + log_err(cd, _("Unsupported resilience mode %s"), params->resilience); + return -EINVAL; + } + + return 0; +} + /* This function must be called with metadata lock held */ static int reencrypt_init(struct crypt_device *cd, const char *name, @@ -2504,17 +2539,21 @@ static int reencrypt_init(struct crypt_device *cd, goto out; } - r = LUKS2_keyslot_reencrypt_allocate(cd, hdr, reencrypt_keyslot, - params); - if (r < 0) - goto out; - r = reencrypt_make_backup_segments(cd, hdr, keyslot_new, _cipher, data_offset, params); if (r) { log_dbg(cd, "Failed to create reencryption backup device segments."); goto out; } + r = reencrypt_verify_resilience_params(cd, params); + if (r < 0) + goto out; + + r = LUKS2_keyslot_reencrypt_allocate(cd, hdr, reencrypt_keyslot, params, + reencrypt_get_alignment(cd, hdr)); + if (r < 0) + goto out; + r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, passphrase, passphrase_size, vks); if (r < 0) goto out;