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[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT,ACTION_BENCHMARK,ACTION_LUKSADDKEY[]
*--key-size, -s* _bits_:: *--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 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. 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. Use _cryptsetup --help_ to show the compiled-in defaults.
endif::[] endif::[]
ifdef::ACTION_REENCRYPT[] ifdef::ACTION_REENCRYPT[]
*LUKS1*: *LUKS2*:
If you are increasing key size, there must be enough space in the LUKS header Provide current key size in _bits_. The argument has to be a multiple of 8.
for enlarged keyslots (data offset must be large enough) or reencryption Useful when specifying the size of current volume key when no keyslot is active.
cannot be performed.
+ +
If there is not enough space for keyslots with new key size, *LUKS1*:
you can destructively shrink device with --reduce-device-size option. See --new-key-size.
endif::[] endif::[]
endif::[] endif::[]
@@ -687,6 +686,24 @@ ifdef::ACTION_LUKSADDKEY[]
Set key description in keyring that will be used for new passphrase retrieval. Set key description in keyring that will be used for new passphrase retrieval.
endif::[] 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[] ifdef::ACTION_LUKSADDKEY[]
*--new-key-slot <0-N>*:: *--new-key-slot <0-N>*::
This option allows you to specify which key slot is selected for 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. Specify what token to use to get the passphrase for a new keyslot.
endif::[] 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[] ifdef::ACTION_OPEN,ACTION_LUKSFORMAT,ACTION_REENCRYPT[]
*--offset, -o <number of 512 byte sectors>*:: *--offset, -o <number of 512 byte sectors>*::
Start offset in the backend device in 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. --key-size parameter as well.
endif::[] endif::[]
ifdef::ACTION_REENCRYPT[] 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::[] 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 *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 do it right. Otherwise, you can end up with a low-entropy or otherwise
partially predictable volume key which will compromise security. 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_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_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_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_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) 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_KEYFILE_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_KEY_DESCRIPTION_ACTIONS { ADDKEY_ACTION } #define OPT_NEW_KEY_DESCRIPTION_ACTIONS { ADDKEY_ACTION }
#define OPT_NEW_KEY_SLOT_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_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_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_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 } #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_FEC_ROOTS "fec-roots"
#define OPT_FORCE_PASSWORD "force-password" #define OPT_FORCE_PASSWORD "force-password"
#define OPT_FORCE_OFFLINE_REENCRYPT "force-offline-reencrypt" #define OPT_FORCE_OFFLINE_REENCRYPT "force-offline-reencrypt"
#define OPT_FORCE_NO_KEYSLOTS "force-no-keyslots"
#define OPT_FORMAT "format" #define OPT_FORMAT "format"
#define OPT_HASH "hash" #define OPT_HASH "hash"
#define OPT_HASH_BLOCK_SIZE "hash-block-size" #define OPT_HASH_BLOCK_SIZE "hash-block-size"
@@ -103,11 +104,14 @@
#define OPT_VOLUME_KEY_KEYRING "volume-key-keyring" #define OPT_VOLUME_KEY_KEYRING "volume-key-keyring"
#define OPT_NEW "new" #define OPT_NEW "new"
#define OPT_NEW_KEY_DESCRIPTION "new-key-description" #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_KEY_SLOT "new-key-slot"
#define OPT_NEW_KEYFILE "new-keyfile" #define OPT_NEW_KEYFILE "new-keyfile"
#define OPT_NEW_KEYFILE_OFFSET "new-keyfile-offset" #define OPT_NEW_KEYFILE_OFFSET "new-keyfile-offset"
#define OPT_NEW_KEYFILE_SIZE "new-keyfile-size" #define OPT_NEW_KEYFILE_SIZE "new-keyfile-size"
#define OPT_NEW_TOKEN_ID "new-token-id" #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_OFFSET "offset"
#define OPT_PANIC_ON_CORRUPTION "panic-on-corruption" #define OPT_PANIC_ON_CORRUPTION "panic-on-corruption"
#define OPT_PBKDF "pbkdf" #define OPT_PBKDF "pbkdf"

View File

@@ -248,6 +248,98 @@ static int reencrypt_hint_force_offline_reencrypt(const char *data_device)
return 0; 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, static int reencrypt_single_key_unlock(struct crypt_device *cd,
const struct crypt_params_reencrypt *params, const struct crypt_params_reencrypt *params,
struct crypt_keyslot_context **r_kc) 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; char *msg;
crypt_reencrypt_info ri; crypt_reencrypt_info ri;
int r; int r;
size_t passwordLen; char *active_name = NULL, *hash = NULL;
char *active_name = NULL, *hash = NULL, *password = NULL;
struct crypt_params_reencrypt params = {}; struct crypt_params_reencrypt params = {};
struct crypt_keyslot_context *kc = NULL, *kc2 = NULL;
ri = crypt_reencrypt_status(cd, &params); ri = crypt_reencrypt_status(cd, &params);
if (ri == CRYPT_REENCRYPT_CRASH) if (ri == CRYPT_REENCRYPT_CRASH)
@@ -362,22 +454,24 @@ static int reencrypt_luks2_load(struct crypt_device *cd, const char *data_device
goto out; goto out;
} }
r = tools_get_key(NULL, &password, &passwordLen, if (params.mode == CRYPT_REENCRYPT_REENCRYPT)
ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), r = reencrypt_multi_key_unlock(cd, &params, &kc, &kc2);
ARG_STR(OPT_KEY_FILE_ID), ARG_UINT32(OPT_TIMEOUT_ID), else
verify_passphrase(0), 0, cd); r = reencrypt_single_key_unlock(cd, &params, &kc);
if (r < 0) if (r < 0)
goto out; goto out;
if (!ARG_SET(OPT_FORCE_OFFLINE_REENCRYPT_ID)) if (!ARG_SET(OPT_FORCE_OFFLINE_REENCRYPT_ID))
r = reencrypt_get_active_name(cd, data_device, &active_name); r = reencrypt_get_active_name(cd, data_device, &active_name);
if (r >= 0) if (r >= 0)
r = crypt_reencrypt_init_by_passphrase(cd, active_name, password, r = crypt_reencrypt_init_by_keyslot_context(cd, active_name, kc, kc2 ?: kc,
passwordLen, ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID),
ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL, &params); ARG_INT32(OPT_NEW_KEY_SLOT_ID),
NULL, NULL, &params);
out: out:
free(hash); free(hash);
crypt_safe_free(password); crypt_keyslot_context_free(kc);
crypt_keyslot_context_free(kc2);
free(active_name); free(active_name);
return r; return r;
} }