Allow device in reencryption to be activated by volume keys in files.

Allow to use up to two --volume-key-file options (paired with
two --key-size options) for device in-reencryption activation.
This commit is contained in:
Ondrej Kozina
2025-05-12 16:40:04 +02:00
parent 5a84dc87e3
commit cbcb8c6ee3
2 changed files with 57 additions and 12 deletions

View File

@@ -543,7 +543,10 @@ current volume key. Also, it is used when new unbound keyslot is created by
specifying --unbound parameter.
endif::[]
ifdef::ACTION_OPEN[]
This option can be used for _plain_ device type only.
This option can be used for _plain_ and _luks_ devices. For LUKS2 devices
in reencryption you may use the parameter twice to specify both old and new volume key
sizes. Each --key-size option corresponds to the respective --volume-key-file parameter
(also allowed to be used up to two times).
endif::[]
ifndef::ACTION_REENCRYPT,ACTION_OPEN,ACTION_LUKSADDKEY[]
This option can be used for _open --type plain_ or _luksFormat_. All
@@ -1313,6 +1316,9 @@ It may be also used when no keyslot is active.
endif::[]
ifdef::ACTION_OPEN[]
This allows one to open _luks_ and _bitlk_ device types without giving a passphrase. +
For devices in reencryption the option may be used twice to specify both old and new volume keys.
When using the option twice make sure you pair each --volume-key-file option with respective
--key-size parameter as well.
endif::[]
ifdef::ACTION_REENCRYPT[]
Use (set) new volume key stored in a file. +
@@ -1330,7 +1336,8 @@ Use a volume key stored in a keyring.
This allows one to open _luks_ and _plain_ device types without giving a passphrase.
+
For LUKS, the key and associated type has to be readable from userspace so that volume
key digest may be verified in before activation.
key digest may be verified in before activation. For devices in reencryption the option may be
used twice to specify both old and new volume keys.
+
For PLAIN type, the user must ensure that the key in the keyring is unchanged since activation.
Otherwise, reloading the key can cause data corruption after an unexpected key change.

View File

@@ -17,11 +17,15 @@
static char *keyfiles[MAX_KEYFILES];
static char *keyring_links[MAX_KEYRING_LINKS];
static char *vks_in_keyring[MAX_VK_IN_KEYRING];
static char *vk_files[2];
static char *keyfile_stdin = NULL;
static uint32_t key_sizes[2];
static int keyfiles_count = 0;
static int keyring_links_count = 0;
static int vks_in_keyring_count = 0;
static int vk_files_count = 0;
static int key_sizes_count = 0;
int64_t data_shift = 0;
const char *device_type = "luks";
@@ -52,6 +56,8 @@ void tools_cleanup(void)
free(keyring_links[--keyring_links_count]);
while (vks_in_keyring_count)
free(vks_in_keyring[--vks_in_keyring_count]);
while (vk_files_count)
free(vk_files[--vk_files_count]);
total_keyfiles = 0;
}
@@ -1784,27 +1790,37 @@ static int action_open_luks(void)
goto out;
}
/*
* When activating device in-reencryption with --volume-key-file or --volume-key-keyring
* the ordering of parameters does not matter. This applies also if any parameter is used
* twice. The library internal code tests both passed keys if they match old or new
* volume key digests and assign them respectively.
*/
if (ARG_SET(OPT_VOLUME_KEY_FILE_ID) || ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) {
if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
keysize = crypt_get_volume_key_size(cd);
if (!keysize && !ARG_SET(OPT_KEY_SIZE_ID)) {
if (vk_files[0] && !vk_files[1]) {
keysize = key_sizes[0] / 8;
if (!keysize)
keysize = crypt_get_volume_key_size(cd);
if (!keysize) /* only in LUKS2 decryption or with no keyslots */
keysize = crypt_get_old_volume_key_size(cd);
if (!keysize) {
log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
r = -EINVAL;
goto out;
} else if (!keysize)
keysize = ARG_UINT32(OPT_KEY_SIZE_ID) / 8;
}
}
} else if (vk_files[0] && vk_files[1])
keysize = key_sizes[0] / 8;
r = luks_init_keyslot_contexts_by_volume_keys(cd, ARG_STR(OPT_VOLUME_KEY_FILE_ID),
NULL /* unused */,
keysize,
0 /* unused */,
r = luks_init_keyslot_contexts_by_volume_keys(cd, vk_files[0], vk_files[1],
keysize, key_sizes[1] / 8,
vks_in_keyring[0],
vks_in_keyring[1],
&kc1, &kc2);
if (r < 0)
goto out;
/* The ordering of kc1 or kc2 does not matter */
r = crypt_activate_by_keyslot_context(cd, activated_name, CRYPT_ANY_SLOT,
kc1, CRYPT_ANY_SLOT, kc2, activate_flags);
if (r == -EPERM)
@@ -3314,6 +3330,9 @@ static const char *verify_open(void)
ARG_SET(OPT_VOLUME_KEY_FILE_ID)) && !strcmp_or_null(device_type, "plain"))
return _("Option --volume-key-keyring cannot be combined with --hash or --volume-key-file.");
if (vk_files[1] && !key_sizes[1])
return _("Both --volume-key-file options must be paired with respective --key-size options.");
/* "open --type tcrypt" and "tcryptDump" checks are identical */
return verify_tcryptdump();
}
@@ -3628,6 +3647,14 @@ static void basic_options_cb(poptContext popt_context,
usage(popt_context, EXIT_FAILURE,
_("Key size must be a multiple of 8 bits"),
poptGetInvocationName(popt_context));
if (key_sizes_count < 2)
key_sizes[key_sizes_count++] = ARG_UINT32(OPT_KEY_SIZE_ID);
else {
usage(popt_context, EXIT_FAILURE,
_("At most 2 key size specifications can be supplied."),
poptGetInvocationName(popt_context));
}
break;
case OPT_INTEGRITY_KEY_SIZE_ID:
if (ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID) == 0)
@@ -3650,6 +3677,17 @@ static void basic_options_cb(poptContext popt_context,
_("Key size must be a multiple of 8 bits"),
poptGetInvocationName(popt_context));
break;
case OPT_VOLUME_KEY_FILE_ID:
if (vk_files_count < 2)
vk_files[vk_files_count++] = strdup(ARG_STR(OPT_VOLUME_KEY_FILE_ID));
else {
if (snprintf(buf, sizeof(buf), _("At most %d volume key specifications can be supplied."), 2) < 0)
buf[0] = '\0';
usage(popt_context, EXIT_FAILURE,
buf,
poptGetInvocationName(popt_context));
}
break;
case OPT_VOLUME_KEY_KEYRING_ID:
if (vks_in_keyring_count < MAX_VK_IN_KEYRING)
vks_in_keyring[vks_in_keyring_count++] = strdup(ARG_STR(OPT_VOLUME_KEY_KEYRING_ID));