Allow reencryption resume by new methods.

The reencryption operation can now be resumed
using tokens or by specifying volume keys
directly.
This commit is contained in:
Ondrej Kozina
2025-05-13 10:31:50 +02:00
parent cbcb8c6ee3
commit 0dc630b911
5 changed files with 178 additions and 19 deletions

View File

@@ -527,7 +527,7 @@ endif::[]
ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT,ACTION_BENCHMARK,ACTION_LUKSADDKEY[]
*--key-size, -s* _bits_::
ifndef::ACTION_LUKSADDKEY[]
ifndef::ACTION_LUKSADDKEY,ACTION_REENCRYPT[]
Sets key size in _bits_. The argument has to be a multiple of 8. The
possible key-sizes are limited by the cipher and mode used.
+
@@ -554,13 +554,12 @@ other LUKS actions will use the key-size specified in the LUKS header.
Use _cryptsetup --help_ to show the compiled-in defaults.
endif::[]
ifdef::ACTION_REENCRYPT[]
*LUKS1*:
If you are increasing key size, there must be enough space in the LUKS header
for enlarged keyslots (data offset must be large enough) or reencryption
cannot be performed.
*LUKS2*:
Provide current key size in _bits_. The argument has to be a multiple of 8.
Useful when specifying the size of current volume key when no keyslot is active.
+
If there is not enough space for keyslots with new key size,
you can destructively shrink device with --reduce-device-size option.
*LUKS1*:
See --new-key-size.
endif::[]
endif::[]
@@ -687,6 +686,24 @@ ifdef::ACTION_LUKSADDKEY[]
Set key description in keyring that will be used for new passphrase retrieval.
endif::[]
ifdef::ACTION_REENCRYPT[]
*--new-key-size* _bits_::
Sets new key size in _bits_. The argument has to be a multiple of 8. The
possible key-sizes are limited by the new cipher and mode used in
reencryption.
+
See /proc/crypto for more information. Note that key-size in
/proc/crypto is stated in bytes.
+
*LUKS1*:
If you are increasing key size, there must be enough space in the LUKS header
for enlarged keyslots (data offset must be large enough) or reencryption
cannot be performed.
+
If there is not enough space for keyslots with new key size,
you can destructively shrink device with --reduce-device-size option.
endif::[]
ifdef::ACTION_LUKSADDKEY[]
*--new-key-slot <0-N>*::
This option allows you to specify which key slot is selected for
@@ -705,6 +722,35 @@ ifdef::ACTION_LUKSADDKEY[]
Specify what token to use to get the passphrase for a new keyslot.
endif::[]
ifdef::ACTION_REENCRYPT[]
*--new-volume-key-file*::
Use (set) new volume key stored in a file. The option must be paired
with --new-key-size parameter when initializing reencryption
operation.
+
*WARNING:* If you create your own volume key, you need to make sure to
do it right. Otherwise, you can end up with a low-entropy or otherwise
partially predictable volume key which will compromise security.
endif::[]
ifdef::ACTION_REENCRYPT[]
*--new-volume-key-keyring* _<key description>_::
Use (set) new volume key stored in a keyring.
+
The size of key stored in a keyring must be compatible with new cipher used
in reencryption operation. See /proc/crypto for more information.
Note that key-size in /proc/crypto is stated in bytes.
+
The _<key description>_ uses keyctl-compatible syntax. This can either be a
numeric key ID or a string name in the format _%<key type>:<key name>_. See
also *KEY IDENTIFIERS* section of *keyctl*(1). When no _%<key type>:_ prefix
is specified we assume the key type is _user_ (default type).
+
*WARNING:* If you create your own volume key, you need to make sure to
do it right. Otherwise, you can end up with a low-entropy or otherwise
partially predictable volume key which will compromise security.
endif::[]
ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT[]
*--offset, -o <number of 512 byte sectors>*::
Start offset in the backend device in 512-byte sectors.
@@ -1321,9 +1367,15 @@ When using the option twice make sure you pair each --volume-key-file option wit
--key-size parameter as well.
endif::[]
ifdef::ACTION_REENCRYPT[]
Use (set) new volume key stored in a file. +
*LUKS2*:
Provides current volume key stored in a file. It can be used to reencrypt the device with
no active keyslot together with --new-volume-key-file or --new-volume-key-keyring options.
+
*LUKS1*:
See --new-volume-key-file.
+
endif::[]
ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY,ACTION_REENCRYPT[]
ifdef::ACTION_LUKSFORMAT,ACTION_LUKSADDKEY[]
*WARNING:* If you create your own volume key, you need to make sure to
do it right. Otherwise, you can end up with a low-entropy or otherwise
partially predictable volume key which will compromise security.

View File

@@ -118,10 +118,16 @@ ARG(OPT_NEW_KEYFILE_SIZE, '\0', POPT_ARG_STRING, N_("Limits the read from newly
ARG(OPT_NEW_KEY_DESCRIPTION, '\0', POPT_ARG_STRING, N_("Keyring new key description"), NULL, CRYPT_ARG_STRING, {}, OPT_NEW_KEY_DESCRIPTION_ACTIONS)
ARG(OPT_NEW_KEY_SIZE, '\0', POPT_ARG_STRING, N_("The size of the new encryption key"), N_("BITS"), CRYPT_ARG_UINT32, {}, OPT_NEW_KEY_SIZE_ACTIONS)
ARG(OPT_NEW_KEY_SLOT, '\0', POPT_ARG_STRING, N_("Slot number for new key (default is first free)"), "INT", CRYPT_ARG_INT32, { .i32_value = CRYPT_ANY_SLOT }, OPT_NEW_KEY_SLOT_ACTIONS)
ARG(OPT_NEW_TOKEN_ID, '\0', POPT_ARG_STRING, N_("Token number (default: any)"), "INT", CRYPT_ARG_INT32, { .i32_value = CRYPT_ANY_TOKEN }, OPT_NEW_TOKEN_ID_ACTIONS)
ARG(OPT_NEW_VOLUME_KEY_FILE, '\0', POPT_ARG_STRING, N_("Use the new volume key from file"), NULL, CRYPT_ARG_STRING, {}, OPT_NEW_VOLUME_KEY_FILE_ACTIONS)
ARG(OPT_NEW_VOLUME_KEY_KEYRING, '\0', POPT_ARG_STRING, N_("Use the specified keyring key as new volume key"), NULL, CRYPT_ARG_STRING, {}, OPT_NEW_VOLUME_KEY_KEYRING_ACTIONS)
ARG(OPT_OFFSET, 'o', POPT_ARG_STRING, N_("The start offset in the backend device"), N_("SECTORS"), CRYPT_ARG_UINT64, {}, OPT_OFFSET_ACTIONS)
ARG(OPT_PBKDF, '\0', POPT_ARG_STRING, N_("PBKDF algorithm (for LUKS2): argon2i, argon2id, pbkdf2"), NULL, CRYPT_ARG_STRING, {}, OPT_PBKDF_ACTIONS)

View File

@@ -70,7 +70,10 @@
#define OPT_NEW_KEYFILE_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_KEY_DESCRIPTION_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_KEY_SLOT_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_KEY_SIZE_ACTIONS { REENCRYPT_ACTION }
#define OPT_NEW_TOKEN_ID_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_VOLUME_KEY_FILE_ACTIONS { REENCRYPT_ACTION }
#define OPT_NEW_VOLUME_KEY_KEYRING_ACTIONS { REENCRYPT_ACTION }
#define OPT_OFFSET_ACTIONS { OPEN_ACTION, REENCRYPT_ACTION, FORMAT_ACTION }
#define OPT_PBKDF_ACTIONS { BENCHMARK_ACTION, FORMAT_ACTION, ADDKEY_ACTION, CHANGEKEY_ACTION, CONVERTKEY_ACTION, REENCRYPT_ACTION }
#define OPT_PBKDF_FORCE_ITERATIONS_ACTIONS { FORMAT_ACTION, ADDKEY_ACTION, CHANGEKEY_ACTION, CONVERTKEY_ACTION, REENCRYPT_ACTION }

View File

@@ -44,6 +44,7 @@
#define OPT_FEC_ROOTS "fec-roots"
#define OPT_FORCE_PASSWORD "force-password"
#define OPT_FORCE_OFFLINE_REENCRYPT "force-offline-reencrypt"
#define OPT_FORCE_NO_KEYSLOTS "force-no-keyslots"
#define OPT_FORMAT "format"
#define OPT_HASH "hash"
#define OPT_HASH_BLOCK_SIZE "hash-block-size"
@@ -103,11 +104,14 @@
#define OPT_VOLUME_KEY_KEYRING "volume-key-keyring"
#define OPT_NEW "new"
#define OPT_NEW_KEY_DESCRIPTION "new-key-description"
#define OPT_NEW_KEY_SIZE "new-key-size"
#define OPT_NEW_KEY_SLOT "new-key-slot"
#define OPT_NEW_KEYFILE "new-keyfile"
#define OPT_NEW_KEYFILE_OFFSET "new-keyfile-offset"
#define OPT_NEW_KEYFILE_SIZE "new-keyfile-size"
#define OPT_NEW_TOKEN_ID "new-token-id"
#define OPT_NEW_VOLUME_KEY_FILE "new-volume-key-file"
#define OPT_NEW_VOLUME_KEY_KEYRING "new-volume-key-keyring"
#define OPT_OFFSET "offset"
#define OPT_PANIC_ON_CORRUPTION "panic-on-corruption"
#define OPT_PBKDF "pbkdf"

View File

@@ -248,6 +248,98 @@ static int reencrypt_hint_force_offline_reencrypt(const char *data_device)
return 0;
}
static int reencrypt_multi_key_unlock(struct crypt_device *cd,
const struct crypt_params_reencrypt *params,
struct crypt_keyslot_context **r_kc1,
struct crypt_keyslot_context **r_kc2)
{
int r, tries, keysize_bytes, new_keysize_bytes;
struct crypt_keyslot_context *kc1 = NULL, *kc2 = NULL;
assert(cd);
assert(params);
assert(r_kc1);
assert(r_kc2);
keysize_bytes = crypt_get_old_volume_key_size(cd);
new_keysize_bytes = crypt_get_volume_key_size(cd);
if (!keysize_bytes && ARG_SET(OPT_KEY_SIZE_ID))
keysize_bytes = ARG_UINT32(OPT_KEY_SIZE_ID) / 8;
if (!new_keysize_bytes && ARG_SET(OPT_NEW_KEY_SIZE_ID))
new_keysize_bytes = ARG_UINT32(OPT_NEW_KEY_SIZE_ID) / 8;
if (ARG_SET(OPT_VOLUME_KEY_FILE_ID) && !keysize_bytes) {
log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
return -EINVAL;
}
if (ARG_SET(OPT_NEW_VOLUME_KEY_FILE_ID) && !new_keysize_bytes) {
log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --new-key-size option."));
return -EINVAL;
}
if (ARG_SET(OPT_VOLUME_KEY_FILE_ID) ||
ARG_SET(OPT_NEW_VOLUME_KEY_FILE_ID) ||
ARG_SET(OPT_VOLUME_KEY_KEYRING_ID) ||
ARG_SET(OPT_NEW_VOLUME_KEY_KEYRING_ID)) {
r = luks_init_keyslot_contexts_by_volume_keys(cd, ARG_STR(OPT_VOLUME_KEY_FILE_ID),
ARG_STR(OPT_NEW_VOLUME_KEY_FILE_ID),
keysize_bytes, new_keysize_bytes,
ARG_STR(OPT_VOLUME_KEY_KEYRING_ID),
ARG_STR(OPT_NEW_VOLUME_KEY_KEYRING_ID),
&kc1, &kc2);
if (r < 0)
return r;
r = crypt_activate_by_keyslot_context(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
kc1, ARG_INT32(OPT_NEW_KEY_SLOT_ID), kc2,
0);
if (r == -EPERM)
log_err(_("Volume key does not match the volume."));
} else {
r = luks_try_token_unlock(cd, ARG_INT32(OPT_KEY_SLOT_ID),
ARG_INT32(OPT_TOKEN_ID_ID), NULL,
ARG_STR(OPT_TOKEN_TYPE_ID), 0,
set_tries_tty(false), true,
ARG_SET(OPT_TOKEN_ONLY_ID) || ARG_SET(OPT_TOKEN_ID_ID) || ARG_SET(OPT_TOKEN_TYPE_ID),
&kc1);
if (r >= 0 || quit || ARG_SET(OPT_TOKEN_ONLY_ID))
goto out;
r = -ENOENT;
tries = set_tries_tty(true);
do {
crypt_keyslot_context_free(kc1);
kc1 = NULL;
r = luks_init_keyslot_context(cd, NULL, verify_passphrase(0), false, &kc1);
if (r < 0)
goto out;
r = crypt_activate_by_keyslot_context(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
kc1, ARG_INT32(OPT_NEW_KEY_SLOT_ID),
kc1, 0);
tools_keyslot_msg(r, UNLOCKED);
tools_passphrase_msg(r);
check_signal(&r);
} while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
}
out:
if (r >= 0) {
*r_kc1 = kc1;
*r_kc2 = kc2;
} else {
crypt_keyslot_context_free(kc1);
crypt_keyslot_context_free(kc2);
}
return r;
}
static int reencrypt_single_key_unlock(struct crypt_device *cd,
const struct crypt_params_reencrypt *params,
struct crypt_keyslot_context **r_kc)
@@ -329,9 +421,9 @@ static int reencrypt_luks2_load(struct crypt_device *cd, const char *data_device
char *msg;
crypt_reencrypt_info ri;
int r;
size_t passwordLen;
char *active_name = NULL, *hash = NULL, *password = NULL;
char *active_name = NULL, *hash = NULL;
struct crypt_params_reencrypt params = {};
struct crypt_keyslot_context *kc = NULL, *kc2 = NULL;
ri = crypt_reencrypt_status(cd, &params);
if (ri == CRYPT_REENCRYPT_CRASH)
@@ -362,22 +454,24 @@ static int reencrypt_luks2_load(struct crypt_device *cd, const char *data_device
goto out;
}
r = tools_get_key(NULL, &password, &passwordLen,
ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID),
ARG_STR(OPT_KEY_FILE_ID), ARG_UINT32(OPT_TIMEOUT_ID),
verify_passphrase(0), 0, cd);
if (params.mode == CRYPT_REENCRYPT_REENCRYPT)
r = reencrypt_multi_key_unlock(cd, &params, &kc, &kc2);
else
r = reencrypt_single_key_unlock(cd, &params, &kc);
if (r < 0)
goto out;
if (!ARG_SET(OPT_FORCE_OFFLINE_REENCRYPT_ID))
r = reencrypt_get_active_name(cd, data_device, &active_name);
if (r >= 0)
r = crypt_reencrypt_init_by_passphrase(cd, active_name, password,
passwordLen, ARG_INT32(OPT_KEY_SLOT_ID),
ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL, &params);
r = crypt_reencrypt_init_by_keyslot_context(cd, active_name, kc, kc2 ?: kc,
ARG_INT32(OPT_KEY_SLOT_ID),
ARG_INT32(OPT_NEW_KEY_SLOT_ID),
NULL, NULL, &params);
out:
free(hash);
crypt_safe_free(password);
crypt_keyslot_context_free(kc);
crypt_keyslot_context_free(kc2);
free(active_name);
return r;
}