Blame SOURCES/0006-Add-clevis-luks-regen-command.patch

95204d
From 158bdeda3ca961b0e615c8adfc58b61e1a1ba5c7 Mon Sep 17 00:00:00 2001
95204d
From: Sergio Correia <scorreia@redhat.com>
95204d
Date: Wed, 13 May 2020 23:55:41 -0300
95204d
Subject: [PATCH 6/8] Add clevis luks regen command
95204d
95204d
The clevis luks regen command regenerates the LUKS binding for a given
95204d
device/slot, using the same configuration of the existing binding.
95204d
95204d
Example:
95204d
95204d
clevis luks list -d /dev/sda1
95204d
1: tang '{"url":"http://tang.server"}'
95204d
2: tpm2 '{"hash":"sha256","key":"ecc"}'
95204d
95204d
To rotate the binding in slot 1, we can use the following:
95204d
clevis luks regen -d /dev/sda1 -s 1
95204d
95204d
The new binding will use the existing configuration, namely:
95204d
'{"url":"http://tang.server"}', with the `tang' pin.
95204d
---
95204d
 src/luks/clevis-luks-common-functions    | 230 +++++++++++++++++++++++
95204d
 src/luks/clevis-luks-pass                |   5 +-
95204d
 src/luks/clevis-luks-regen               | 185 ++++++++++++++++++
95204d
 src/luks/clevis-luks-regen.1.adoc        |  48 +++++
95204d
 src/luks/meson.build                     |   3 +
95204d
 src/luks/tests/backup-restore-luks1      | 114 +++++++++++
95204d
 src/luks/tests/backup-restore-luks2      | 115 ++++++++++++
95204d
 src/luks/tests/meson.build               |   6 +
95204d
 src/luks/tests/meson.build.orig          | 110 +++++++++++
95204d
 src/luks/tests/regen-inplace-luks1       |  98 ++++++++++
95204d
 src/luks/tests/regen-inplace-luks2       |  99 ++++++++++
95204d
 src/luks/tests/regen-not-inplace-luks1   |  95 ++++++++++
95204d
 src/luks/tests/regen-not-inplace-luks2   |  96 ++++++++++
95204d
 src/luks/tests/tests-common-functions.in |  26 +++
95204d
 14 files changed, 1228 insertions(+), 2 deletions(-)
95204d
 create mode 100755 src/luks/clevis-luks-regen
95204d
 create mode 100644 src/luks/clevis-luks-regen.1.adoc
95204d
 create mode 100755 src/luks/tests/backup-restore-luks1
95204d
 create mode 100755 src/luks/tests/backup-restore-luks2
95204d
 create mode 100644 src/luks/tests/meson.build.orig
95204d
 create mode 100755 src/luks/tests/regen-inplace-luks1
95204d
 create mode 100755 src/luks/tests/regen-inplace-luks2
95204d
 create mode 100755 src/luks/tests/regen-not-inplace-luks1
95204d
 create mode 100755 src/luks/tests/regen-not-inplace-luks2
95204d
95204d
diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions
95204d
index 36f0bfd..5b515ad 100644
95204d
--- a/src/luks/clevis-luks-common-functions
95204d
+++ b/src/luks/clevis-luks-common-functions
95204d
@@ -325,3 +325,233 @@ clevis_luks_unlock_device() {
95204d
 
95204d
     return 1
95204d
 }
95204d
+
95204d
+# Generate a key with the same entropy as the LUKS master key of a given
95204d
+# device.
95204d
+generate_key() {
95204d
+    local DEV="${1}"
95204d
+
95204d
+    if [ -z "${DEV}" ]; then
95204d
+        echo "Please, specify a device." >&2
95204d
+        return 1
95204d
+    fi
95204d
+
95204d
+    local dump
95204d
+    local filter
95204d
+    dump=$(cryptsetup luksDump "${DEV}")
95204d
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
95204d
+        filter=$(sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p' <<< "${dump}")
95204d
+    elif cryptsetup isLuks --type luks2 "${DEV}"; then
95204d
+        filter=$(sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p' <<< "${dump}")
95204d
+    else
95204d
+        echo "${DEV} is not a supported LUKS device!" >&2
95204d
+        return 1
95204d
+    fi
95204d
+    local bits
95204d
+    bits=$(sort -n <<< "${filter}" | tail -n 1)
95204d
+    pwmake "${bits}"
95204d
+}
95204d
+
95204d
+# clevis_luks1_save_slot() works with LUKS1 devices and it saves a given JWE
95204d
+# to a specific device and slot. The last parameter indicates whether we
95204d
+# should overwrite existing metadata.
95204d
+clevis_luks1_save_slot() {
95204d
+    local DEV="${1}"
95204d
+    local SLOT="${2}"
95204d
+    local JWE="${3}"
95204d
+    local OVERWRITE="${4}"
95204d
+
95204d
+    ! luksmeta test -d "${DEV}" && return 1
95204d
+
95204d
+    local UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
95204d
+    if luksmeta load -d "${DEV}" -s "${SLOT}" -u "${UUID}" >/dev/null 2>/dev/null; then
95204d
+        [ -z "${OVERWRITE}" ] && return 1
95204d
+        if ! luksmeta wipe -f -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then
95204d
+            echo "Error wiping slot ${SLOT} from ${DEV}" >&2
95204d
+            return 1
95204d
+        fi
95204d
+    fi
95204d
+
95204d
+    if ! echo -n "${jwe}" | luksmeta save -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then
95204d
+        echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2
95204d
+        return 1
95204d
+    fi
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks2_save_slot() works with LUKS2 devices and it saves a given JWE
95204d
+# to a specific device and slot. The last parameter indicates whether we
95204d
+# should overwrite existing metadata.
95204d
+clevis_luks2_save_slot() {
95204d
+    local DEV="${1}"
95204d
+    local SLOT="${2}"
95204d
+    local JWE="${3}"
95204d
+    local OVERWRITE="${4}"
95204d
+
95204d
+    local dump token
95204d
+    dump="$(cryptsetup luksDump "${DEV}")"
95204d
+    if ! token="$(grep -E -B1 "^\s+Keyslot:\s+${SLOT}$" <<< "${dump}" \
95204d
+            | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"; then
95204d
+        echo "Error trying to read token from LUKS2 device ${DEV}, slot ${SLOT}" >&2
95204d
+        return 1
95204d
+    fi
95204d
+
95204d
+    if [ -n "${token}" ]; then
95204d
+        [ -z "${OVERWRITE}" ] && return 1
95204d
+        if ! cryptsetup token remove --token-id "${token}" "${DEV}"; then
95204d
+            echo "Error while removing token ${token} from LUKS2 device ${DEV}" >&2
95204d
+            return 1
95204d
+        fi
95204d
+    fi
95204d
+
95204d
+    local metadata
95204d
+    metadata=$(printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' \
95204d
+               "${SLOT}" "$(jose jwe fmt -i- <<< "${JWE}")")
95204d
+    if ! cryptsetup token import "${DEV}" <<< "${metadata}"; then
95204d
+        echo "Error saving metadata to LUKS2 header in device ${DEV}" >&2
95204d
+        return 1
95204d
+    fi
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks_save_slot() saves a given JWE to a LUKS device+slot. It can also
95204d
+# overwrite existing metadata.
95204d
+clevis_luks_save_slot() {
95204d
+    local DEV="${1}"
95204d
+    local SLOT="${2}"
95204d
+    local JWE="${3}"
95204d
+    local OVERWRITE="${4}"
95204d
+
95204d
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
95204d
+        ! clevis_luks1_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \
95204d
+            && return 1
95204d
+    elif cryptsetup isLuks --type luks2 "${DEV}"; then
95204d
+        ! clevis_luks2_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \
95204d
+            && return 1
95204d
+    else
95204d
+        return 1
95204d
+    fi
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks1_backup_dev() backups the LUKSMeta slots from a LUKS device,
95204d
+# which can be restored with clevis_luks1_restore_dev().
95204d
+clevis_luks1_backup_dev() {
95204d
+    local DEV="${1}"
95204d
+    local TMP="${2}"
95204d
+
95204d
+    [ -z "${DEV}" ] && return 1
95204d
+    [ -z "${TMP}" ] && return 1
95204d
+
95204d
+    local slots slt uuid jwe fname
95204d
+    readarray -t slots < <(cryptsetup luksDump "${DEV}" \
95204d
+                           | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
95204d
+
95204d
+    for slt in "${slots[@]}"; do
95204d
+        if ! uuid=$(luksmeta show -d "${DEV}" -s "${slt}") \
95204d
+                    || [ -z "${uuid}" ]; then
95204d
+            continue
95204d
+        fi
95204d
+        if ! jwe=$(luksmeta load -d "${DEV}" -s "${slt}") \
95204d
+                   || [ -z "${jwe}" ]; then
95204d
+            continue
95204d
+        fi
95204d
+
95204d
+        fname=$(printf "slot_%s_%s" "${slt}" "${uuid}")
95204d
+        printf "%s" "${jwe}" > "${TMP}/${fname}"
95204d
+    done
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks1_restore_dev() takes care of restoring the LUKSMeta slots from
95204d
+# a LUKS device that was backup'ed by clevis_luks1_backup_dev().
95204d
+clevis_luks1_restore_dev() {
95204d
+    local DEV="${1}"
95204d
+    local TMP="${2}"
95204d
+
95204d
+    [ -z "${DEV}" ] && return 1
95204d
+    [ -z "${TMP}" ] && return 1
95204d
+
95204d
+    local slt uuid jwe fname
95204d
+    for fname in "${TMP}"/slot_*; do
95204d
+        [ -f "${fname}" ] || break
95204d
+        if ! slt=$(echo "${fname}" | cut -d '_' -f 2) \
95204d
+                   || [ -z "${slt}" ]; then
95204d
+            continue
95204d
+        fi
95204d
+        if ! uuid=$(echo "${fname}" | cut -d '_' -f 3) \
95204d
+                    || [ -z "${uuid}" ]; then
95204d
+            continue
95204d
+        fi
95204d
+        if ! jwe=$(< "${fname}") || [ -z "${jwe}" ]; then
95204d
+            continue
95204d
+        fi
95204d
+        if ! clevis_luks1_save_slot "${DEV}" "${slt}" \
95204d
+                                    "${jwe}" "overwrite"; then
95204d
+            echo "Error restoring LUKSmeta slot ${slt} from ${DEV}" >&2
95204d
+            return 1
95204d
+        fi
95204d
+    done
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks_backup_dev() backups a particular LUKS device, which can then
95204d
+# be restored with clevis_luks_restore_dev().
95204d
+clevis_luks_backup_dev() {
95204d
+    local DEV="${1}"
95204d
+    local TMP="${2}"
95204d
+
95204d
+    [ -z "${DEV}" ] && return 1
95204d
+    [ -z "${TMP}" ] && return 1
95204d
+
95204d
+    local HDR
95204d
+    HDR="${TMP}/$(basename "${DEV}").header"
95204d
+    if ! cryptsetup luksHeaderBackup "${DEV}" --batch-mode \
95204d
+            --header-backup-file "${HDR}"; then
95204d
+        echo "Error backing up LUKS header from ${DEV}" >&2
95204d
+        return 1
95204d
+    fi
95204d
+
95204d
+    # If LUKS1, we need to manually back up (and later restore) the
95204d
+    # LUKSmeta slots. For LUKS2, simply saving the header also saves
95204d
+    # the associated tokens.
95204d
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
95204d
+        if ! clevis_luks1_backup_dev "${DEV}" "${TMP}"; then
95204d
+            return 1
95204d
+        fi
95204d
+    fi
95204d
+    return 0
95204d
+}
95204d
+
95204d
+# clevis_luks_restore_dev() restores a given device that was backup'ed by
95204d
+# clevis_luks_backup_dev().
95204d
+clevis_luks_restore_dev() {
95204d
+    local DEV="${1}"
95204d
+    local TMP="${2}"
95204d
+
95204d
+    [ -z "${DEV}" ] && return 1
95204d
+    [ -z "${TMP}" ] && return 1
95204d
+
95204d
+    local HDR
95204d
+    HDR="${TMP}/$(basename "${DEV}").header"
95204d
+    if [ ! -e "${HDR}" ]; then
95204d
+        echo "LUKS header backup does not exist" >&2
95204d
+        return 1
95204d
+    fi
95204d
+
95204d
+    if ! cryptsetup luksHeaderRestore "${DEV}" --batch-mode \
95204d
+            --header-backup-file "${HDR}"; then
95204d
+        echo "Error restoring LUKS header from ${DEV}" >&2
95204d
+        return 1
95204d
+    fi
95204d
+
95204d
+    # If LUKS1, we need to manually back up (and later restore) the
95204d
+    # LUKSmeta slots. For LUKS2, simply saving the header also saves
95204d
+    # the associated tokens.
95204d
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
95204d
+        if ! clevis_luks1_restore_dev "${DEV}" "${TMP}"; then
95204d
+            return 1
95204d
+        fi
95204d
+    fi
95204d
+    return 0
95204d
+}
95204d
diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass
95204d
index 1ce8c4c..d31bc17 100755
95204d
--- a/src/luks/clevis-luks-pass
95204d
+++ b/src/luks/clevis-luks-pass
95204d
@@ -63,7 +63,8 @@ if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then
95204d
     exit 1
95204d
 fi
95204d
 
95204d
-if ! clevis decrypt < <(echo -n "${jwe}"); then
95204d
-    echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2
95204d
+if ! passphrase=$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null); then
95204d
+    echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in ${DEV}!" >&2
95204d
     exit 1
95204d
 fi
95204d
+echo -n "${passphrase}"
95204d
diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen
95204d
new file mode 100755
95204d
index 0000000..44fd673
95204d
--- /dev/null
95204d
+++ b/src/luks/clevis-luks-regen
95204d
@@ -0,0 +1,185 @@
95204d
+#!/usr/bin/bash
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2018 Red Hat, Inc.
95204d
+# Author: Radovan Sroka <rsroka@redhat.com>
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+. clevis-luks-common-functions
95204d
+
95204d
+SUMMARY="Regenerate LUKS metadata"
95204d
+
95204d
+if [ "$1" == "--summary" ]; then
95204d
+    echo "$SUMMARY"
95204d
+    exit 0
95204d
+fi
95204d
+
95204d
+function usage_and_exit () {
95204d
+    exec >&2
95204d
+    echo "Usage: clevis luks regen -d DEV -s SLOT"
95204d
+    echo
95204d
+    echo "$SUMMARY"
95204d
+    echo
95204d
+    echo "  -d DEV  The LUKS device on which to perform rebinding"
95204d
+    echo
95204d
+    echo "  -s SLT  The LUKS slot to use"
95204d
+    echo
95204d
+    exit "${1}"
95204d
+}
95204d
+
95204d
+on_exit() {
95204d
+    if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then
95204d
+        echo "Delete temporary files failed!" >&2
95204d
+        echo "You need to clean up: ${TMP}" >&2
95204d
+        exit 1
95204d
+    fi
95204d
+}
95204d
+
95204d
+while getopts ":hfd:s:" o; do
95204d
+    case "$o" in
95204d
+    d) DEV="$OPTARG";;
95204d
+    h) usage_and_exit 0;;
95204d
+    s) SLT="$OPTARG";;
95204d
+    *) usage_and_exit 1;;
95204d
+    esac
95204d
+done
95204d
+
95204d
+if [ -z "$DEV" ]; then
95204d
+    echo "Did not specify a device!" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+if [ -z "$SLT" ]; then
95204d
+    echo "Did not specify a slot!" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+### ----------------------------------------------------------------------
95204d
+if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then
95204d
+    echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+PIN=$(echo "${pin_cfg}" | awk '{ print $2 }')
95204d
+CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'")
95204d
+
95204d
+echo "Regenerating with:"
95204d
+echo "PIN: $PIN"
95204d
+echo "CONFIG: $CFG"
95204d
+
95204d
+trap 'echo "Ignoring CONTROL-C!"' INT TERM
95204d
+
95204d
+# Get the existing key.
95204d
+if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then
95204d
+    # We failed to obtain the passphrase for the slot -- perhaps
95204d
+    # it was rotated? --, so let's request user input.
95204d
+    read -r -s -p "Enter existing LUKS password: " existing_key; echo
95204d
+fi
95204d
+
95204d
+# Check if the key is valid.
95204d
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+# Check if we can do the update in-place, i.e., if the key we got is the one
95204d
+# for the slot we are interested in.
95204d
+in_place=
95204d
+if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \
95204d
+        <<< "${existing_key}"; then
95204d
+    in_place=true
95204d
+fi
95204d
+
95204d
+# Create new key.
95204d
+if ! new_passphrase=$(generate_key "${DEV}"); then
95204d
+    echo "Error generating new key for device ${DEV}" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+# Reencrypt the new password.
95204d
+if ! jwe=$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}"); then
95204d
+    echo "Error using pin '${PIN}' with config '${CFG}'" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+# Updating the metadata and the actual passphrase are destructive operations,
95204d
+# hence we will do a backup of the LUKS header and restore it later in case
95204d
+# we have issues performing these operations.
95204d
+if ! TMP="$(mktemp -d)"; then
95204d
+    echo "Creating a temporary dir for device backup/restore failed!" >&2
95204d
+    exit 1
95204d
+fi
95204d
+trap 'on_exit' EXIT
95204d
+
95204d
+# Backup LUKS header.
95204d
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
95204d
+    echo "Error while trying to back up LUKS header from ${DEV}" >&2
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+restore_device() {
95204d
+    local DEV="${1}"
95204d
+    local TMP="${2}"
95204d
+
95204d
+    if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
95204d
+        echo "Error while trying to restore LUKS header from ${DEV}." >&2
95204d
+    else
95204d
+        echo "LUKS header restored successfully." >&2
95204d
+    fi
95204d
+}
95204d
+
95204d
+# Update the key slot with the new key. If we have the key for this slot,
95204d
+# the change happens in-place. Otherwise, we kill the slot and re-add it.
95204d
+if [ -n "${in_place}" ]; then
95204d
+    if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \
95204d
+            <(echo -n "${new_passphrase}") <<< "${existing_key}"; then
95204d
+        echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2
95204d
+        restore_device "${DEV}" "${TMP}"
95204d
+        exit 1
95204d
+    fi
95204d
+else
95204d
+    if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then
95204d
+        echo "Error wiping slot ${SLT} from ${DEV}" >&2
95204d
+        restore_device "${DEV}" "${TMP}"
95204d
+        exit 1
95204d
+    fi
95204d
+
95204d
+    if ! echo -n "${new_passphrase}" \
95204d
+            | cryptsetup luksAddKey --key-slot "${SLT}" \
95204d
+                         --key-file <(echo -n "${existing_key}") "${DEV}"; then
95204d
+        echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2
95204d
+        restore_device "${DEV}" "${TMP}"
95204d
+        exit 1
95204d
+    fi
95204d
+fi
95204d
+
95204d
+# Update the metadata.
95204d
+if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then
95204d
+    echo "Error updating metadata in ${DEV}:${SLT}" >&2
95204d
+    restore_device "${DEV}" "${TMP}"
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+# Now make sure that we can unlock this device after the change.
95204d
+# If we can't, undo the changes.
95204d
+if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \
95204d
+        <<< $(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then
95204d
+    echo "Invalid configuration detected after rebinding. Reverting changes."
95204d
+    restore_device "${DEV}" "${TMP}"
95204d
+    exit 1
95204d
+fi
95204d
+
95204d
+echo "Keys were succesfully rotated."
95204d
diff --git a/src/luks/clevis-luks-regen.1.adoc b/src/luks/clevis-luks-regen.1.adoc
95204d
new file mode 100644
95204d
index 0000000..763fa1e
95204d
--- /dev/null
95204d
+++ b/src/luks/clevis-luks-regen.1.adoc
95204d
@@ -0,0 +1,48 @@
95204d
+CLEVIS-LUKS-REGEN(1)
95204d
+=====================
95204d
+:doctype: manpage
95204d
+
95204d
+
95204d
+== NAME
95204d
+
95204d
+clevis-luks-regen - Regenerates LUKS binding
95204d
+
95204d
+== SYNOPSIS
95204d
+
95204d
+*clevis luks regen* -d DEV -s SLT
95204d
+
95204d
+== OVERVIEW
95204d
+
95204d
+The *clevis luks regen* command regenerates the LUKS binding for a given slot in a LUKS device, using the same configuration of the
95204d
+existing binding. Its operation can be compared to performing *clevis luks unbind* and *clevis luks bind* for rebinding said slot and device.
95204d
+This is useful when rotating keys.
95204d
+
95204d
+== OPTIONS
95204d
+
95204d
+* *-d* _DEV_ :
95204d
+  The bound LUKS device
95204d
+
95204d
+* *-s* _SLT_ :
95204d
+  The slot or key slot number for rebinding. Note that it requires that such slot is currently bound by clevis.
95204d
+
95204d
+== EXAMPLE
95204d
+
95204d
+    Let's start by using clevis luks list to see the current binding configuration in /dev/sda1:
95204d
+
95204d
+    # clevis luks list -d /dev/sda1
95204d
+    1: tang '{"url":"http://tang.server"}'
95204d
+    2: tpm2 '{"hash":"sha256","key":"ecc"}'
95204d
+
95204d
+    We see that slot 1 in /dev/sda1 has a tang binding with the following configuration:
95204d
+    '{"url":"http://tang.server"}'
95204d
+
95204d
+    Now let's do the rebinding of slot 1:
95204d
+    # clevis luks regen -d /dev/sda1 -s 1
95204d
+
95204d
+    After a successful operation, we will have the new binding using the same configuration that was already in place.
95204d
+
95204d
+== SEE ALSO
95204d
+
95204d
+link:clevis-luks-list.1.adoc[*clevis-luks-list*(1)]
95204d
+link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)]
95204d
+link:clevis-luks-unbind.1.adoc[*clevis-luks-unbind*(1)]
95204d
diff --git a/src/luks/meson.build b/src/luks/meson.build
95204d
index fda2ca8..f21388d 100644
95204d
--- a/src/luks/meson.build
95204d
+++ b/src/luks/meson.build
95204d
@@ -44,6 +44,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found()
95204d
 
95204d
   bins += join_paths(meson.current_source_dir(), 'clevis-luks-pass')
95204d
   mans += join_paths(meson.current_source_dir(), 'clevis-luks-pass.1')
95204d
+
95204d
+  bins += join_paths(meson.current_source_dir(), 'clevis-luks-regen')
95204d
+  mans += join_paths(meson.current_source_dir(), 'clevis-luks-regen.1')
95204d
 else
95204d
   warning('Will not install LUKS support due to missing dependencies!')
95204d
 endif
95204d
diff --git a/src/luks/tests/backup-restore-luks1 b/src/luks/tests/backup-restore-luks1
95204d
new file mode 100755
95204d
index 0000000..733a4b6
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/backup-restore-luks1
95204d
@@ -0,0 +1,114 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+. clevis-luks-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS1.
95204d
+DEV="${TMP}/luks1-device"
95204d
+new_device "luks1" "${DEV}"
95204d
+
95204d
+SLT=5
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+ORIG_DEV="${TMP}/device.orig"
95204d
+# Let's save it for comparison later.
95204d
+cp -f "${DEV}" "${ORIG_DEV}"
95204d
+
95204d
+# Now let's backup and restore.
95204d
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
95204d
+    error "${TEST}: backup of ${DEV} failed."
95204d
+fi
95204d
+
95204d
+# Now let's remove both the binding and the initial passphrase.
95204d
+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: unbind of slot ${SLT} in ${DEV} failed."
95204d
+fi
95204d
+
95204d
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: error removing the default password from ${DEV}."
95204d
+fi
95204d
+
95204d
+# Making sure we have no slots enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
95204d
+if [ "${enabled}" -ne 0 ]; then
95204d
+    error "${TEST}: we should not have any enabled (${enabled}) slots."
95204d
+fi
95204d
+
95204d
+# Now we can restore it.
95204d
+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
95204d
+    error "${TEST}: error restoring ${DEV}."
95204d
+fi
95204d
+
95204d
+# And compare whether they are the same.
95204d
+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then
95204d
+    error "${TEST}: the device differs from the original one after the restore."
95204d
+fi
95204d
+
95204d
+# And making sure both the default passphrase and the binding work.
95204d
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: the default passphrase for ${DEV} did no work."
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/backup-restore-luks2 b/src/luks/tests/backup-restore-luks2
95204d
new file mode 100755
95204d
index 0000000..a3b8608
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/backup-restore-luks2
95204d
@@ -0,0 +1,115 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+. clevis-luks-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS2.
95204d
+DEV="${TMP}/luks2-device"
95204d
+new_device "luks2" "${DEV}"
95204d
+
95204d
+SLT=5
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+ORIG_DEV="${TMP}/device.orig"
95204d
+# Let's save it for comparison later.
95204d
+cp -f "${DEV}" "${ORIG_DEV}"
95204d
+
95204d
+# Now let's backup and restore.
95204d
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
95204d
+    error "${TEST}: backup of ${DEV} failed."
95204d
+fi
95204d
+
95204d
+# Now let's remove both the binding and the initial passphrase.
95204d
+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: unbind of slot ${SLT} in ${DEV} failed."
95204d
+fi
95204d
+
95204d
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: error removing the default password from ${DEV}."
95204d
+fi
95204d
+
95204d
+# Making sure we have no slots enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" \
95204d
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
95204d
+if [ "${enabled}" -ne 0 ]; then
95204d
+    error "${TEST}: we should not have any enabled (${enabled}) slots."
95204d
+fi
95204d
+
95204d
+# Now we can restore it.
95204d
+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
95204d
+    error "${TEST}: error restoring ${DEV}."
95204d
+fi
95204d
+
95204d
+# And compare whether they are the same.
95204d
+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then
95204d
+    error "${TEST}: the device differs from the original one after the restore."
95204d
+fi
95204d
+
95204d
+# And making sure both the default passphrase and the binding work.
95204d
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: the default passphrase for ${DEV} did no work."
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build
95204d
index 4757c4b..dbef9bf 100644
95204d
--- a/src/luks/tests/meson.build
95204d
+++ b/src/luks/tests/meson.build
95204d
@@ -87,6 +87,9 @@ if has_tang
95204d
   test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90)
95204d
 endif
95204d
 test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env)
95204d
+test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env)
95204d
+test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90)
95204d
+test('regen-not-inplace-luks1', find_program('regen-not-inplace-luks1'), env: env, timeout: 90)
95204d
 
95204d
 # LUKS2 tests go here, and they get included if we get support for it, based
95204d
 # on the cryptsetup version.
95204d
@@ -107,4 +110,7 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0'
95204d
     test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120)
95204d
   endif
95204d
   test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60)
95204d
+  test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90)
95204d
+  test('regen-inplace-luks2', find_program('regen-inplace-luks2'), env: env, timeout: 90)
95204d
+  test('regen-not-inplace-luks2', find_program('regen-not-inplace-luks2'), env: env, timeout: 90)
95204d
 endif
95204d
diff --git a/src/luks/tests/meson.build.orig b/src/luks/tests/meson.build.orig
95204d
new file mode 100644
95204d
index 0000000..4757c4b
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/meson.build.orig
95204d
@@ -0,0 +1,110 @@
95204d
+actv = find_program(
95204d
+  'systemd-socket-activate',
95204d
+  'systemd-activate',
95204d
+  required: false
95204d
+)
95204d
+
95204d
+# We use jq for comparing the pin config in the clevis luks list tests.
95204d
+jq = find_program('jq', required: false)
95204d
+
95204d
+# we use systemd-socket-activate for running test tang servers.
95204d
+actv = find_program(
95204d
+  'systemd-socket-activate',
95204d
+  'systemd-activate',
95204d
+  required: false
95204d
+)
95204d
+
95204d
+kgen = find_program(
95204d
+  join_paths(libexecdir, 'tangd-keygen'),
95204d
+  join_paths(get_option('prefix'), get_option('libdir'), 'tangd-keygen'),
95204d
+  join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd-keygen'),
95204d
+  join_paths('/', 'usr', get_option('libdir'), 'tangd-keygen'),
95204d
+  join_paths('/', 'usr', get_option('libexecdir'), 'tangd-keygen'),
95204d
+  required: false
95204d
+)
95204d
+tang = find_program(
95204d
+  join_paths(libexecdir, 'tangd'),
95204d
+  join_paths(get_option('prefix'), get_option('libdir'), 'tangd'),
95204d
+  join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd'),
95204d
+  join_paths('/', 'usr', get_option('libdir'), 'tangd'),
95204d
+  join_paths('/', 'usr', get_option('libexecdir'), 'tangd'),
95204d
+  required: false
95204d
+)
95204d
+
95204d
+common_functions = configure_file(input: 'tests-common-functions.in',
95204d
+  output: 'tests-common-functions',
95204d
+  configuration: luksmeta_data,
95204d
+  install: false
95204d
+)
95204d
+
95204d
+env = environment()
95204d
+env.prepend('PATH',
95204d
+  join_paths(meson.source_root(), 'src'),
95204d
+  join_paths(meson.source_root(), 'src', 'luks'),
95204d
+  join_paths(meson.source_root(), 'src', 'pins', 'sss'),
95204d
+  join_paths(meson.source_root(), 'src', 'pins', 'tang'),
95204d
+  join_paths(meson.source_root(), 'src', 'pins', 'tpm2'),
95204d
+  meson.current_source_dir(),
95204d
+  meson.current_build_dir(),
95204d
+  join_paths(meson.build_root(), 'src'),
95204d
+  join_paths(meson.build_root(), 'src', 'luks'),
95204d
+  join_paths(meson.build_root(), 'src', 'pins', 'sss'),
95204d
+  join_paths(meson.build_root(), 'src', 'pins', 'tang'),
95204d
+  join_paths(meson.build_root(), 'src', 'pins', 'tpm2'),
95204d
+  libexecdir,
95204d
+  '/usr/libexec',
95204d
+  separator: ':'
95204d
+)
95204d
+env.set('SD_ACTIVATE', actv.path())
95204d
+
95204d
+has_tang = false
95204d
+if actv.found() and kgen.found() and tang.found()
95204d
+  has_tang = true
95204d
+  env.set('SD_ACTIVATE', actv.path())
95204d
+  env.set('TANGD_KEYGEN', kgen.path())
95204d
+  env.set('TANGD', tang.path())
95204d
+endif
95204d
+
95204d
+test('bind-wrong-pass-luks1', find_program('bind-wrong-pass-luks1'), env: env)
95204d
+test('bind-luks1', find_program('bind-luks1'), env: env)
95204d
+test('unbind-unbound-slot-luks1', find_program('unbind-unbound-slot-luks1'), env: env)
95204d
+test('unbind-luks1', find_program('unbind-luks1'), env: env)
95204d
+test('bind-key-file-non-interactive', find_program('bind-key-file-non-interactive-luks1'), env: env)
95204d
+test('bind-pass-with-newline', find_program('bind-pass-with-newline-luks1'), env: env)
95204d
+test('bind-pass-with-newline-keyfile', find_program('bind-pass-with-newline-keyfile-luks1'), env: env)
95204d
+# Bug #70.
95204d
+test('bind-already-used-luksmeta-slot', find_program('bind-already-used-luksmeta-slot'), env: env, timeout: 60)
95204d
+
95204d
+if jq.found()
95204d
+  test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env)
95204d
+  test('list-tang-luks1', find_program('list-tang-luks1'), env: env)
95204d
+  test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env)
95204d
+else
95204d
+  warning('Will not run "clevis luks list" tests due to missing jq dependency')
95204d
+endif
95204d
+
95204d
+if has_tang
95204d
+  test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90)
95204d
+endif
95204d
+test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env)
95204d
+
95204d
+# LUKS2 tests go here, and they get included if we get support for it, based
95204d
+# on the cryptsetup version.
95204d
+# Binding LUKS2 takes longer, so timeout is increased for a few tests.
95204d
+if luksmeta_data.get('OLD_CRYPTSETUP') == '0'
95204d
+  test('bind-wrong-pass-luks2', find_program('bind-wrong-pass-luks2'), env: env)
95204d
+  test('bind-luks2', find_program('bind-luks2'), env: env, timeout: 60)
95204d
+  test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env)
95204d
+  test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60)
95204d
+
95204d
+  if jq.found()
95204d
+    test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60)
95204d
+    test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60)
95204d
+    test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60)
95204d
+  endif
95204d
+
95204d
+  if has_tang
95204d
+    test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120)
95204d
+  endif
95204d
+  test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60)
95204d
+endif
95204d
diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1
95204d
new file mode 100755
95204d
index 0000000..3a42ced
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/regen-inplace-luks1
95204d
@@ -0,0 +1,98 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS1.
95204d
+DEV="${TMP}/luks1-device"
95204d
+new_device "luks1" "${DEV}"
95204d
+
95204d
+SLT=1
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+# Now let's remove the initial passphrase.
95204d
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: error removing the default password from ${DEV}."
95204d
+fi
95204d
+
95204d
+# Making sure we have a single slot enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
95204d
+if [ "${enabled}" -ne 1 ]; then
95204d
+    error "${TEST}: we should have only one slot enabled (${enabled})."
95204d
+fi
95204d
+
95204d
+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
95204d
+
95204d
+# Now let's try regen.
95204d
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: clevis luks regen failed"
95204d
+fi
95204d
+
95204d
+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
95204d
+
95204d
+if [ "${old_key}" = "${new_key}" ]; then
95204d
+    error "${TEST}: the passphrases should be different"
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2
95204d
new file mode 100755
95204d
index 0000000..1cb7a29
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/regen-inplace-luks2
95204d
@@ -0,0 +1,99 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS2.
95204d
+DEV="${TMP}/luks2-device"
95204d
+new_device "luks2" "${DEV}"
95204d
+
95204d
+SLT=1
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+# Now let's remove the initial passphrase.
95204d
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: error removing the default password from ${DEV}."
95204d
+fi
95204d
+
95204d
+# Making sure we have a single slot enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" \
95204d
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
95204d
+if [ "${enabled}" -ne 1 ]; then
95204d
+    error "${TEST}: we should have only one slot enabled (${enabled})."
95204d
+fi
95204d
+
95204d
+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
95204d
+
95204d
+# Now let's try regen.
95204d
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: clevis luks regen failed"
95204d
+fi
95204d
+
95204d
+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
95204d
+
95204d
+if [ "${old_key}" = "${new_key}" ]; then
95204d
+    error "${TEST}: the passphrases should be different"
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1
95204d
new file mode 100755
95204d
index 0000000..1b65ca7
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/regen-not-inplace-luks1
95204d
@@ -0,0 +1,95 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS1.
95204d
+DEV="${TMP}/luks1-device"
95204d
+new_device "luks1" "${DEV}"
95204d
+
95204d
+SLT=1
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+# Now let's rotate the keys in the server and remove the old ones, so that we
95204d
+# will be unable to unlock the volume using clevis and will have to provide
95204d
+# manually a password for clevis luks regen.
95204d
+rm -rf "${TMP}"/db/*
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Making sure we have two slots enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
95204d
+if [ "${enabled}" -ne 2 ]; then
95204d
+    error "${TEST}: we should have two slots enabled (${enabled})."
95204d
+fi
95204d
+
95204d
+# Now let's try regen.
95204d
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: clevis luks regen failed"
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2
95204d
new file mode 100755
95204d
index 0000000..dc91449
95204d
--- /dev/null
95204d
+++ b/src/luks/tests/regen-not-inplace-luks2
95204d
@@ -0,0 +1,96 @@
95204d
+#!/bin/bash -x
95204d
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
95204d
+#
95204d
+# Copyright (c) 2020 Red Hat, Inc.
95204d
+# Author: Sergio Correia <scorreia@redhat.com>
95204d
+#
95204d
+# This program is free software: you can redistribute it and/or modify
95204d
+# it under the terms of the GNU General Public License as published by
95204d
+# the Free Software Foundation, either version 3 of the License, or
95204d
+# (at your option) any later version.
95204d
+#
95204d
+# This program is distributed in the hope that it will be useful,
95204d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
95204d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
95204d
+# GNU General Public License for more details.
95204d
+#
95204d
+# You should have received a copy of the GNU General Public License
95204d
+# along with this program.  If not, see <http: www.gnu.org="" licenses=""/>.
95204d
+#
95204d
+
95204d
+TEST="${0}"
95204d
+. tests-common-functions
95204d
+
95204d
+function on_exit() {
95204d
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
95204d
+    [ -d "$TMP" ] && rm -rf $TMP
95204d
+}
95204d
+
95204d
+trap 'on_exit' EXIT
95204d
+trap 'exit' ERR
95204d
+
95204d
+export TMP=$(mktemp -d)
95204d
+mkdir -p "${TMP}/db"
95204d
+
95204d
+# Generate the server keys
95204d
+KEYS="$TMP/db"
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+    KEYS="${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Start the server.
95204d
+port=$(shuf -i 1024-65536 -n 1)
95204d
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
95204d
+export PID=$!
95204d
+sleep 0.25
95204d
+
95204d
+url="http://localhost:${port}"
95204d
+adv="${TMP}/adv"
95204d
+curl "${url}/adv" -o "${adv}"
95204d
+
95204d
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
95204d
+
95204d
+# LUKS2.
95204d
+DEV="${TMP}/luks2-device"
95204d
+new_device "luks2" "${DEV}"
95204d
+
95204d
+SLT=1
95204d
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: Bind should have succeeded."
95204d
+fi
95204d
+
95204d
+# Now let's rotate the keys in the server and remove the old ones, so that we
95204d
+# will be unable to unlock the volume using clevis and will have to provide
95204d
+# manually a password for clevis luks regen.
95204d
+rm -rf "${TMP}"/db/*
95204d
+tangd-keygen $TMP/db sig exc
95204d
+if which tangd-update; then
95204d
+    mkdir -p "${TMP}/cache"
95204d
+    tangd-update "${TMP}/db" "${TMP}/cache"
95204d
+fi
95204d
+
95204d
+# Making sure we have two slots enabled.
95204d
+enabled=$(cryptsetup luksDump "${DEV}" \
95204d
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
95204d
+if [ "${enabled}" -ne 2 ]; then
95204d
+    error "${TEST}: we should have two slots enabled (${enabled})."
95204d
+fi
95204d
+
95204d
+# Now let's try regen.
95204d
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
95204d
+    error "${TEST}: clevis luks regen failed"
95204d
+fi
95204d
+
95204d
+TEST_DEV="test-device-${RANDOM}"
95204d
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
95204d
+    error "${TEST}: we were unable to unlock ${DEV}."
95204d
+fi
95204d
+
95204d
+cryptsetup close "${TEST_DEV}"
95204d
+
95204d
+kill -9 "${PID}"
95204d
+! wait "${PID}"
95204d
+unset PID
95204d
diff --git a/src/luks/tests/tests-common-functions.in b/src/luks/tests/tests-common-functions.in
95204d
index 7b3fdad..6101f28 100755
95204d
--- a/src/luks/tests/tests-common-functions.in
95204d
+++ b/src/luks/tests/tests-common-functions.in
95204d
@@ -229,5 +229,31 @@ tang_get_adv() {
95204d
     curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv
95204d
 }
95204d
 
95204d
+# Regenerate binding.
95204d
+clevis_regen() {
95204d
+    local DEV="${1}"
95204d
+    local SLT="${2}"
95204d
+    local PASS="${3}"
95204d
+
95204d
+    expect -d << CLEVIS_REGEN
95204d
+        set timeout 120
95204d
+        spawn sh -c "clevis luks regen -d $DEV -s $SLT"
95204d
+        expect {
95204d
+            "Enter existing LUKS password" {
95204d
+                send "$PASS\r"
95204d
+                exp_continue
95204d
+            }
95204d
+            "Do you wish to trust these keys" {
95204d
+                send "y\r"
95204d
+                exp_continue
95204d
+            }
95204d
+            expect eof
95204d
+            wait
95204d
+        }
95204d
+CLEVIS_REGEN
95204d
+    ret=$?
95204d
+    return "${ret}"
95204d
+}
95204d
+
95204d
 export TANG_HOST=127.0.0.1
95204d
 export DEFAULT_PASS='just-some-test-password-here'
95204d
-- 
95204d
2.18.4
95204d