diff --git a/man/cryptsetup-reencrypt.8 b/man/cryptsetup-reencrypt.8 index 896ad039..ee9c49ab 100644 --- a/man/cryptsetup-reencrypt.8 +++ b/man/cryptsetup-reencrypt.8 @@ -67,6 +67,9 @@ you can destructively shrink device with \-\-reduce-device-size option. .TP .B "\-\-hash, \-h \fI\fR" Specifies the hash used in the LUKS key setup scheme and volume key digest. + +NOTE: if this parameter is not specified, default hash algorithm is always used +for new device header. .TP .B "\-\-iter-time, \-i \fI\fR" The number of milliseconds to spend with PBKDF2 passphrase processing for the @@ -100,6 +103,12 @@ Read a maximum of \fIvalue\fR bytes from the key file. Default is to read the whole file up to the compiled-in maximum. .TP +.B "\-\-keep-key" +Do not change encryption key, just reencrypt the LUKS header and keyslots. + +This option can be combined only with \fI\-\-hash\fR or \fI\-\-iter-time\fR +options. +.TP .B "\-\-tries, \-T" Number of retries for invalid passphrase entry. .TP diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c index 6349cb50..588925db 100644 --- a/src/cryptsetup_reencrypt.c +++ b/src/cryptsetup_reencrypt.c @@ -47,6 +47,7 @@ static int opt_tries = 3; static int opt_key_slot = CRYPT_ANY_SLOT; static int opt_key_size = 0; static int opt_new = 0; +static int opt_keep_key = 0; static const char *opt_reduce_size_str = NULL; static uint64_t opt_reduce_size = 0; @@ -424,7 +425,8 @@ out: static int create_new_header(struct reenc_ctx *rc, const char *cipher, const char *cipher_mode, const char *uuid, - int key_size, struct crypt_params_luks1 *params) + const char *key, int key_size, + struct crypt_params_luks1 *params) { struct crypt_device *cd_new = NULL; int i, r; @@ -441,7 +443,7 @@ static int create_new_header(struct reenc_ctx *rc, const char *cipher, crypt_set_iteration_time(cd_new, opt_iteration_time); if ((r = crypt_format(cd_new, CRYPT_LUKS1, cipher, cipher_mode, - uuid, NULL, key_size, params))) + uuid, key, key_size, params))) goto out; log_verbose(_("New LUKS header for device %s created.\n"), rc->device); @@ -464,6 +466,8 @@ static int backup_luks_headers(struct reenc_ctx *rc) struct crypt_device *cd = NULL; struct crypt_params_luks1 params = {0}; char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; + char *old_key = NULL; + size_t old_key_size; int r; log_dbg("Creating LUKS header backup for device %s.", rc->device); @@ -494,14 +498,30 @@ static int backup_luks_headers(struct reenc_ctx *rc) } } + if (opt_keep_key) { + log_dbg("Keeping key from old header."); + old_key_size = crypt_get_volume_key_size(cd); + old_key = crypt_safe_alloc(old_key_size); + if (!old_key) { + r = -ENOMEM; + goto out; + } + r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, old_key, &old_key_size, + rc->p[rc->keyslot].password, rc->p[rc->keyslot].passwordLen); + if (r < 0) + goto out; + } + r = create_new_header(rc, opt_cipher ? cipher : crypt_get_cipher(cd), opt_cipher ? cipher_mode : crypt_get_cipher_mode(cd), crypt_get_uuid(cd), + old_key, opt_key_size ? opt_key_size / 8 : crypt_get_volume_key_size(cd), ¶ms); out: crypt_free(cd); + crypt_safe_free(old_key); if (r) log_err(_("Creation of LUKS backup headers failed.\n")); return r; @@ -559,7 +579,7 @@ static int backup_fake_header(struct reenc_ctx *rc) r = create_new_header(rc, opt_cipher ? cipher : DEFAULT_LUKS1_CIPHER, opt_cipher ? cipher_mode : DEFAULT_LUKS1_MODE, - NULL, + NULL, NULL, (opt_key_size ? opt_key_size : DEFAULT_LUKS1_KEYBITS) / 8, ¶ms); out: @@ -1087,11 +1107,15 @@ static int run_reencrypt(const char *device) goto out; } - if ((r = activate_luks_headers(&rc))) - goto out; + if (!opt_keep_key) { + log_dbg("Running data area reencryption."); + if ((r = activate_luks_headers(&rc))) + goto out; - if ((r = copy_data(&rc))) - goto out; + if ((r = copy_data(&rc))) + goto out; + } else + log_dbg("Keeping existing key, skipping data area reencryption."); r = restore_luks_header(&rc); out: @@ -1130,6 +1154,7 @@ int main(int argc, const char **argv) { "cipher", 'c', POPT_ARG_STRING, &opt_cipher, 0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL }, { "key-size", 's', POPT_ARG_INT, &opt_key_size, 0, N_("The size of the encryption key"), N_("BITS") }, { "hash", 'h', POPT_ARG_STRING, &opt_hash, 0, N_("The hash used to create the encryption key from the passphrase"), NULL }, + { "keep-key", '\0', POPT_ARG_NONE, &opt_keep_key, 0, N_("Do not change key, no data area reencryption."), NULL }, { "key-file", 'd', POPT_ARG_STRING, &opt_key_file, 0, N_("Read the key from a file."), NULL }, { "iter-time", 'i', POPT_ARG_INT, &opt_iteration_time, 0, N_("PBKDF2 iteration time for LUKS (in ms)"), N_("msecs") }, { "batch-mode", 'q', POPT_ARG_NONE, &opt_batch_mode, 0, N_("Do not ask for confirmation"), NULL }, @@ -1235,6 +1260,10 @@ int main(int argc, const char **argv) usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce-device-size."), poptGetInvocationName(popt_context)); + if (opt_keep_key && ((!opt_hash && !opt_iteration_time) || opt_cipher || opt_new)) + usage(popt_context, EXIT_FAILURE, _("Option --keep-key can be used only with --hash or --iter-time."), + poptGetInvocationName(popt_context)); + if (opt_debug) { opt_verbose = 1; crypt_set_debug_level(-1); diff --git a/tests/reencryption-compat-test b/tests/reencryption-compat-test index 676d3a6b..deecede3 100755 --- a/tests/reencryption-compat-test +++ b/tests/reencryption-compat-test @@ -255,5 +255,16 @@ add_scsi_device sector_size=512 physblk_exp=3 dev_size_mb=8 simple_scsi_reenc "[4096/512 sector]" echo "[OK]" +echo "[8] Header only reencryption (hash and iteration time)" +echo $PWD1 | $CRYPTSETUP -q luksFormat --hash sha1 $LOOPDEV1 || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --hash sha256 --iter-time 1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --hash sha512 +check_hash $PWD1 $HASH1 +echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --iter-time 1 +check_hash $PWD1 $HASH1 + remove_mapping exit 0