Blame SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch

524513
From 5536f15b9235cb6ae1b79a5ad1d96a8ea97b3113 Mon Sep 17 00:00:00 2001
524513
From: Sergio Correia <scorreia@redhat.com>
524513
Date: Wed, 29 Jan 2020 06:29:32 -0500
524513
Subject: [PATCH] Improve clevis luks regen; no unbind in every case
524513
524513
When updating the metadata -- likely due to a tang key rotation --,
524513
clevis will not do unbind + bind in every case.
524513
524513
Now we have 2 cases to be handled:
524513
1) we have the key for the slot being rotated; in this case, the
524513
   rotation happens in-place
524513
524513
2) we don't have the key for the slot being rotated; in this case,
524513
   we have to re-add the keyslot with updated info.
524513
524513
Added also mechanisms for backup + restore of the LUKS header/slots,
524513
so that we can revert back to the original state if clevis luks regen
524513
fails.
524513
---
524513
 src/luks/clevis-luks-common-functions  | 202 ++++++++++++++++++++++
524513
 src/luks/clevis-luks-pass              |   5 +-
524513
 src/luks/clevis-luks-regen             | 223 ++++++++++++-------------
524513
 src/luks/tests/backup-restore-luks1    | 114 +++++++++++++
524513
 src/luks/tests/backup-restore-luks2    | 115 +++++++++++++
524513
 src/luks/tests/meson.build             |   7 +
524513
 src/luks/tests/regen-inplace-luks1     |  98 +++++++++++
524513
 src/luks/tests/regen-inplace-luks2     |  99 +++++++++++
524513
 src/luks/tests/regen-not-inplace-luks1 |  95 +++++++++++
524513
 src/luks/tests/regen-not-inplace-luks2 |  96 +++++++++++
524513
 src/luks/tests/tests-common-functions  |  27 ++-
524513
 11 files changed, 966 insertions(+), 115 deletions(-)
524513
 create mode 100755 src/luks/tests/backup-restore-luks1
524513
 create mode 100755 src/luks/tests/backup-restore-luks2
524513
 create mode 100755 src/luks/tests/regen-inplace-luks1
524513
 create mode 100755 src/luks/tests/regen-inplace-luks2
524513
 create mode 100755 src/luks/tests/regen-not-inplace-luks1
524513
 create mode 100755 src/luks/tests/regen-not-inplace-luks2
524513
524513
diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions
524513
index 9ba1812..2a1af26 100644
524513
--- a/src/luks/clevis-luks-common-functions
524513
+++ b/src/luks/clevis-luks-common-functions
524513
@@ -314,3 +314,205 @@ clevis_luks_read_pins_from_slot() {
524513
     fi
524513
     printf "%s: %s\n" "${SLOT}" "${cfg}"
524513
 }
524513
+
524513
+# clevis_luks1_save_slot() works with LUKS1 devices and it saves a given JWE
524513
+# to a specific device and slot. The last parameter indicates whether we
524513
+# should overwrite existing metadata.
524513
+clevis_luks1_save_slot() {
524513
+    local DEV="${1}"
524513
+    local SLOT="${2}"
524513
+    local JWE="${3}"
524513
+    local OVERWRITE="${4}"
524513
+
524513
+    ! luksmeta test -d "${DEV}" && return 1
524513
+
524513
+    local UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
524513
+    if luksmeta load -d "${DEV}" -s "${SLOT}" -u "${UUID}" >/dev/null 2>/dev/null; then
524513
+        [ -z "${OVERWRITE}" ] && return 1
524513
+        if ! luksmeta wipe -f -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then
524513
+            echo "Error wiping slot ${SLOT} from ${DEV}" >&2
524513
+            return 1
524513
+        fi
524513
+    fi
524513
+
524513
+    if ! echo -n "${jwe}" | luksmeta save -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then
524513
+        echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2
524513
+        return 1
524513
+    fi
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks2_save_slot() works with LUKS2 devices and it saves a given JWE
524513
+# to a specific device and slot. The last parameter indicates whether we
524513
+# should overwrite existing metadata.
524513
+clevis_luks2_save_slot() {
524513
+    local DEV="${1}"
524513
+    local SLOT="${2}"
524513
+    local JWE="${3}"
524513
+    local OVERWRITE="${4}"
524513
+
524513
+    local dump token
524513
+    dump="$(cryptsetup luksDump "${DEV}")"
524513
+    if ! token="$(grep -E -B1 "^\s+Keyslot:\s+${SLOT}$" <<< "${dump}" \
524513
+            | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"; then
524513
+        echo "Error trying to read token from LUKS2 device ${DEV}, slot ${SLOT}" >&2
524513
+        return 1
524513
+    fi
524513
+
524513
+    if [ -n "${token}" ]; then
524513
+        [ -z "${OVERWRITE}" ] && return 1
524513
+        if ! cryptsetup token remove --token-id "${token}" "${DEV}"; then
524513
+            echo "Error while removing token ${token} from LUKS2 device ${DEV}" >&2
524513
+            return 1
524513
+        fi
524513
+    fi
524513
+
524513
+    local metadata
524513
+    metadata=$(printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' \
524513
+               "${SLOT}" "$(jose jwe fmt -i- <<< "${JWE}")")
524513
+    if ! cryptsetup token import "${DEV}" <<< "${metadata}"; then
524513
+        echo "Error saving metadata to LUKS2 header in device ${DEV}" >&2
524513
+        return 1
524513
+    fi
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks_save_slot() saves a given JWE to a LUKS device+slot. It can also
524513
+# overwrite existing metadata.
524513
+clevis_luks_save_slot() {
524513
+    local DEV="${1}"
524513
+    local SLOT="${2}"
524513
+    local JWE="${3}"
524513
+    local OVERWRITE="${4}"
524513
+
524513
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
524513
+        ! clevis_luks1_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \
524513
+            && return 1
524513
+    elif cryptsetup isLuks --type luks2 "${DEV}"; then
524513
+        ! clevis_luks2_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \
524513
+            && return 1
524513
+    else
524513
+        return 1
524513
+    fi
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks1_backup_dev() backups the LUKSMeta slots from a LUKS device,
524513
+# which can be restored with clevis_luks1_restore_dev().
524513
+clevis_luks1_backup_dev() {
524513
+    local DEV="${1}"
524513
+    local TMP="${2}"
524513
+
524513
+    [ -z "${DEV}" ] && return 1
524513
+    [ -z "${TMP}" ] && return 1
524513
+
524513
+    local slots slt uuid jwe fname
524513
+    readarray -t slots < <(cryptsetup luksDump "${DEV}" \
524513
+                           | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
524513
+
524513
+    for slt in "${slots[@]}"; do
524513
+        if ! uuid=$(luksmeta show -d "${DEV}" -s "${slt}") \
524513
+                    || [ -z "${uuid}" ]; then
524513
+            continue
524513
+        fi
524513
+        if ! jwe=$(luksmeta load -d "${DEV}" -s "${slt}") \
524513
+                   || [ -z "${jwe}" ]; then
524513
+            continue
524513
+        fi
524513
+
524513
+        fname=$(printf "slot_%s_%s" "${slt}" "${uuid}")
524513
+        printf "%s" "${jwe}" > "${TMP}/${fname}"
524513
+    done
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks1_restore_dev() takes care of restoring the LUKSMeta slots from
524513
+# a LUKS device that was backup'ed by clevis_luks1_backup_dev().
524513
+clevis_luks1_restore_dev() {
524513
+    local DEV="${1}"
524513
+    local TMP="${2}"
524513
+
524513
+    [ -z "${DEV}" ] && return 1
524513
+    [ -z "${TMP}" ] && return 1
524513
+
524513
+    local slt uuid jwe fname
524513
+    for fname in "${TMP}"/slot_*; do
524513
+        [ -f "${fname}" ] || break
524513
+        if ! slt=$(echo "${fname}" | cut -d '_' -f 2) \
524513
+                   || [ -z "${slt}" ]; then
524513
+            continue
524513
+        fi
524513
+        if ! uuid=$(echo "${fname}" | cut -d '_' -f 3) \
524513
+                    || [ -z "${uuid}" ]; then
524513
+            continue
524513
+        fi
524513
+        if ! jwe=$(< "${fname}") || [ -z "${jwe}" ]; then
524513
+            continue
524513
+        fi
524513
+        if ! clevis_luks1_save_slot "${DEV}" "${slt}" \
524513
+                                    "${jwe}" "overwrite"; then
524513
+            echo "Error restoring LUKSmeta slot ${slt} from ${DEV}" >&2
524513
+            return 1
524513
+        fi
524513
+    done
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks_backup_dev() backups a particular LUKS device, which can then
524513
+# be restored with clevis_luks_restore_dev().
524513
+clevis_luks_backup_dev() {
524513
+    local DEV="${1}"
524513
+    local TMP="${2}"
524513
+
524513
+    [ -z "${DEV}" ] && return 1
524513
+    [ -z "${TMP}" ] && return 1
524513
+
524513
+    local HDR="${TMP}/$(basename "${DEV}").header"
524513
+    if ! cryptsetup luksHeaderBackup "${DEV}" --batch-mode \
524513
+            --header-backup-file "${HDR}"; then
524513
+        echo "Error backing up LUKS header from ${DEV}" >&2
524513
+        return 1
524513
+    fi
524513
+
524513
+    # If LUKS1, we need to manually back up (and later restore) the
524513
+    # LUKSmeta slots. For LUKS2, simply saving the header also saves
524513
+    # the associated tokens.
524513
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
524513
+        if ! clevis_luks1_backup_dev "${DEV}" "${TMP}"; then
524513
+            return 1
524513
+        fi
524513
+    fi
524513
+    return 0
524513
+}
524513
+
524513
+# clevis_luks_restore_dev() restores a given device that was backup'ed by
524513
+# clevis_luks_backup_dev().
524513
+clevis_luks_restore_dev() {
524513
+    local DEV="${1}"
524513
+    local TMP="${2}"
524513
+
524513
+    [ -z "${DEV}" ] && return 1
524513
+    [ -z "${TMP}" ] && return 1
524513
+
524513
+    local HDR="${TMP}/$(basename "${DEV}").header"
524513
+    if [ ! -e "${HDR}" ]; then
524513
+        echo "LUKS header backup does not exist" >&2
524513
+        return 1
524513
+    fi
524513
+
524513
+    if ! cryptsetup luksHeaderRestore "${DEV}" --batch-mode \
524513
+            --header-backup-file "${HDR}"; then
524513
+        echo "Error restoring LUKS header from ${DEV}" >&2
524513
+        return 1
524513
+    fi
524513
+
524513
+    # If LUKS1, we need to manually back up (and later restore) the
524513
+    # LUKSmeta slots. For LUKS2, simply saving the header also saves
524513
+    # the associated tokens.
524513
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
524513
+        if ! clevis_luks1_restore_dev "${DEV}" "${TMP}"; then
524513
+            return 1
524513
+        fi
524513
+    fi
524513
+    return 0
524513
+}
524513
diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass
524513
index 1ce8c4c..d31bc17 100755
524513
--- a/src/luks/clevis-luks-pass
524513
+++ b/src/luks/clevis-luks-pass
524513
@@ -63,7 +63,8 @@ if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then
524513
     exit 1
524513
 fi
524513
 
524513
-if ! clevis decrypt < <(echo -n "${jwe}"); then
524513
-    echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2
524513
+if ! passphrase=$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null); then
524513
+    echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in ${DEV}!" >&2
524513
     exit 1
524513
 fi
524513
+echo -n "${passphrase}"
524513
diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen
524513
index 9535ba3..44fd673 100755
524513
--- a/src/luks/clevis-luks-regen
524513
+++ b/src/luks/clevis-luks-regen
524513
@@ -1,8 +1,9 @@
524513
-#!/usr/bin/env bash
524513
+#!/usr/bin/bash
524513
 # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
 #
524513
 # Copyright (c) 2018 Red Hat, Inc.
524513
 # Author: Radovan Sroka <rsroka@redhat.com>
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
 #
524513
 # This program is free software: you can redistribute it and/or modify
524513
 # it under the terms of the GNU General Public License as published by
524513
@@ -28,19 +29,27 @@ if [ "$1" == "--summary" ]; then
524513
 fi
524513
 
524513
 function usage_and_exit () {
524513
-    echo >&2
524513
-    echo "Usage: clevis luks regen -d DEV -s SLOT" >&2
524513
-    echo >&2
524513
-    echo "$SUMMARY" >&2
524513
-    echo >&2
524513
-    exit "$1"
524513
+    exec >&2
524513
+    echo "Usage: clevis luks regen -d DEV -s SLOT"
524513
+    echo
524513
+    echo "$SUMMARY"
524513
+    echo
524513
+    echo "  -d DEV  The LUKS device on which to perform rebinding"
524513
+    echo
524513
+    echo "  -s SLT  The LUKS slot to use"
524513
+    echo
524513
+    exit "${1}"
524513
 }
524513
 
524513
-if [ "$#" -ne "4" ]; then
524513
-    usage_and_exit 1
524513
-fi
524513
+on_exit() {
524513
+    if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then
524513
+        echo "Delete temporary files failed!" >&2
524513
+        echo "You need to clean up: ${TMP}" >&2
524513
+        exit 1
524513
+    fi
524513
+}
524513
 
524513
-while getopts "hd:s:" o; do
524513
+while getopts ":hfd:s:" o; do
524513
     case "$o" in
524513
     d) DEV="$OPTARG";;
524513
     h) usage_and_exit 0;;
524513
@@ -49,88 +58,6 @@ while getopts "hd:s:" o; do
524513
     esac
524513
 done
524513
 
524513
-function decode_luks_header () {
524513
-    if DATA_CODED="$(jose jwe fmt -i- <<< "$1")"; then
524513
-        DATA_CODED="$(jose fmt -j- -g protected -u- <<< "$DATA_CODED")"
524513
-        DATA_DECODED="$(jose b64 dec -i- <<< "$DATA_CODED")"
524513
-    else
524513
-        echo "Error decoding JWE protected header!" >&2
524513
-        exit 1
524513
-    fi
524513
-
524513
-    echo "$DATA_DECODED"
524513
-}
524513
-
524513
-function generate_cfg () {
524513
-    echo -n "{"
524513
-    DATA="$(decode_luks_header "$1")"
524513
-
524513
-    if ! P="$(jose fmt -j- -g clevis -g pin -u- <<< "$DATA")" || [ -z "$P" ]; then
524513
-        echo "Pin wasn't found in LUKS metadata!" >&2
524513
-        exit 1
524513
-    fi
524513
-
524513
-    if ! CONTENT="$(jose fmt -j- -g clevis -g "$P" -o- <<< "$DATA")" || [ -z "$CONTENT" ]; then
524513
-        echo "Content was not found!" >&2
524513
-    fi
524513
-
524513
-    # echo -n "\"$P\": ["
524513
-
524513
-    if [ "$P" = "tang" ] || [ "$P" = "http" ]; then
524513
-        URL="$(jose fmt -j- -g url -u- <<< "$CONTENT")"
524513
-        echo -n "\"url\":\"$URL\""
524513
-    elif [ "$P" = "sss" ]; then
524513
-        THRESHOLD="$(jose fmt -j- -g t -o- <<< "$CONTENT")"
524513
-        if [ -n "$THRESHOLD" ]; then
524513
-            echo -n "\"t\":$THRESHOLD,"
524513
-        fi
524513
-
524513
-        echo -n "\"pins\":{"
524513
-
524513
-        CNT=0
524513
-        PREV=""
524513
-        while ITEM="$(jose fmt -j- -g jwe -g"$CNT" -u- <<< "$CONTENT")"; do
524513
-            if [ -z "$ITEM" ]; then
524513
-                CNT=$(( CNT + 1 ))
524513
-                continue # in some cases it can be empty string
524513
-            fi
524513
-
524513
-            DD="$(decode_luks_header "$ITEM")"
524513
-
524513
-            if ! PP="$(jose fmt -j- -g clevis -g pin -u- <<< "$DD")" || [ -z "$PP" ]; then
524513
-                echo "Pin wasn't found in LUKS metadata!" >&2
524513
-                exit 1
524513
-            fi
524513
-
524513
-            if [ "$CNT" -eq 0 ]; then
524513
-                PREV="$PP"
524513
-                echo -n "\"$PP\":["
524513
-                echo -n "$(generate_cfg "$ITEM")"
524513
-            else
524513
-                if ! [ "$PREV" = "$PP" ]; then
524513
-                    echo -n "],\"$PP\":["
524513
-                    echo -n "$(generate_cfg "$ITEM")"
524513
-                else
524513
-                    echo -n ",$(generate_cfg "$ITEM")"
524513
-                fi
524513
-            fi
524513
-
524513
-            PREV="$PP"
524513
-            CNT=$(( CNT + 1 ))
524513
-        done
524513
-
524513
-        echo -n "]}"
524513
-
524513
-    else
524513
-        echo "Unknown pin $P!" >&2
524513
-        exit 1
524513
-    fi
524513
-
524513
-    echo -n "}"
524513
-}
524513
-
524513
-### get luks metadata
524513
-
524513
 if [ -z "$DEV" ]; then
524513
     echo "Did not specify a device!" >&2
524513
     exit 1
524513
@@ -141,23 +68,14 @@ if [ -z "$SLT" ]; then
524513
     exit 1
524513
 fi
524513
 
524513
-if ! OLD_LUKS_CODED="$(clevis_luks_read_slot "$DEV" "$SLT")"; then
524513
-    echo "Error reading metadata from LUKS device!" >&2
524513
-    exit 1
524513
-fi
524513
-
524513
 ### ----------------------------------------------------------------------
524513
-
524513
-DECODED="$(decode_luks_header "$OLD_LUKS_CODED")"
524513
-
524513
-if ! PIN="$(jose fmt -j- -g clevis -g pin -u- <<< "$DECODED")" || [ -z "$PIN" ]; then
524513
-    echo "Pin wasn't found in LUKS metadata!" >&2
524513
+if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then
524513
+    echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2
524513
     exit 1
524513
 fi
524513
 
524513
-CFG="$(generate_cfg "$OLD_LUKS_CODED")"
524513
-
524513
-### ----------------------------------------------------------------------
524513
+PIN=$(echo "${pin_cfg}" | awk '{ print $2 }')
524513
+CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'")
524513
 
524513
 echo "Regenerating with:"
524513
 echo "PIN: $PIN"
524513
@@ -166,20 +84,101 @@ echo "CONFIG: $CFG"
524513
 trap 'echo "Ignoring CONTROL-C!"' INT TERM
524513
 
524513
 # Get the existing key.
524513
-read -r -s -p "Enter existing LUKS password: " existing_key; echo
524513
+if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then
524513
+    # We failed to obtain the passphrase for the slot -- perhaps
524513
+    # it was rotated? --, so let's request user input.
524513
+    read -r -s -p "Enter existing LUKS password: " existing_key; echo
524513
+fi
524513
 
524513
 # Check if the key is valid.
524513
-if ! cryptsetup luksOpen --test-passphrase "${DEV}" <<< "${existing_key}"; then
524513
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then
524513
+    exit 1
524513
+fi
524513
+
524513
+# Check if we can do the update in-place, i.e., if the key we got is the one
524513
+# for the slot we are interested in.
524513
+in_place=
524513
+if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \
524513
+        <<< "${existing_key}"; then
524513
+    in_place=true
524513
+fi
524513
+
524513
+# Create new key.
524513
+if ! new_passphrase=$(generate_key "${DEV}"); then
524513
+    echo "Error generating new key for device ${DEV}" >&2
524513
+    exit 1
524513
+fi
524513
+
524513
+# Reencrypt the new password.
524513
+if ! jwe=$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}"); then
524513
+    echo "Error using pin '${PIN}' with config '${CFG}'" >&2
524513
+    exit 1
524513
+fi
524513
+
524513
+# Updating the metadata and the actual passphrase are destructive operations,
524513
+# hence we will do a backup of the LUKS header and restore it later in case
524513
+# we have issues performing these operations.
524513
+if ! TMP="$(mktemp -d)"; then
524513
+    echo "Creating a temporary dir for device backup/restore failed!" >&2
524513
+    exit 1
524513
+fi
524513
+trap 'on_exit' EXIT
524513
+
524513
+# Backup LUKS header.
524513
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
524513
+    echo "Error while trying to back up LUKS header from ${DEV}" >&2
524513
     exit 1
524513
 fi
524513
 
524513
-if ! clevis luks unbind -d "${DEV}" -s "${SLT}" -f; then
524513
-    echo "Error during unbind of rotated key from slot:$SLT in $DEV" >&2
524513
+restore_device() {
524513
+    local DEV="${1}"
524513
+    local TMP="${2}"
524513
+
524513
+    if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
524513
+        echo "Error while trying to restore LUKS header from ${DEV}." >&2
524513
+    else
524513
+        echo "LUKS header restored successfully." >&2
524513
+    fi
524513
+}
524513
+
524513
+# Update the key slot with the new key. If we have the key for this slot,
524513
+# the change happens in-place. Otherwise, we kill the slot and re-add it.
524513
+if [ -n "${in_place}" ]; then
524513
+    if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \
524513
+            <(echo -n "${new_passphrase}") <<< "${existing_key}"; then
524513
+        echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2
524513
+        restore_device "${DEV}" "${TMP}"
524513
+        exit 1
524513
+    fi
524513
+else
524513
+    if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then
524513
+        echo "Error wiping slot ${SLT} from ${DEV}" >&2
524513
+        restore_device "${DEV}" "${TMP}"
524513
+        exit 1
524513
+    fi
524513
+
524513
+    if ! echo -n "${new_passphrase}" \
524513
+            | cryptsetup luksAddKey --key-slot "${SLT}" \
524513
+                         --key-file <(echo -n "${existing_key}") "${DEV}"; then
524513
+        echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2
524513
+        restore_device "${DEV}" "${TMP}"
524513
+        exit 1
524513
+    fi
524513
+fi
524513
+
524513
+# Update the metadata.
524513
+if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then
524513
+    echo "Error updating metadata in ${DEV}:${SLT}" >&2
524513
+    restore_device "${DEV}" "${TMP}"
524513
     exit 1
524513
 fi
524513
 
524513
-if ! clevis luks bind -d "${DEV}" -s "${SLT}" "${PIN}" "${CFG}" -k - <<< "${existing_key}"; then
524513
-    echo "Error during bind of new key from slot:$SLT in $DEV" >&2
524513
+# Now make sure that we can unlock this device after the change.
524513
+# If we can't, undo the changes.
524513
+if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \
524513
+        <<< $(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then
524513
+    echo "Invalid configuration detected after rebinding. Reverting changes."
524513
+    restore_device "${DEV}" "${TMP}"
524513
     exit 1
524513
 fi
524513
 
524513
diff --git a/src/luks/tests/backup-restore-luks1 b/src/luks/tests/backup-restore-luks1
524513
new file mode 100755
524513
index 0000000..733a4b6
524513
--- /dev/null
524513
+++ b/src/luks/tests/backup-restore-luks1
524513
@@ -0,0 +1,114 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+. clevis-luks-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS1.
524513
+DEV="${TMP}/luks1-device"
524513
+new_device "luks1" "${DEV}"
524513
+
524513
+SLT=5
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+ORIG_DEV="${TMP}/device.orig"
524513
+# Let's save it for comparison later.
524513
+cp -f "${DEV}" "${ORIG_DEV}"
524513
+
524513
+# Now let's backup and restore.
524513
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
524513
+    error "${TEST}: backup of ${DEV} failed."
524513
+fi
524513
+
524513
+# Now let's remove both the binding and the initial passphrase.
524513
+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: unbind of slot ${SLT} in ${DEV} failed."
524513
+fi
524513
+
524513
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: error removing the default password from ${DEV}."
524513
+fi
524513
+
524513
+# Making sure we have no slots enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
524513
+if [ "${enabled}" -ne 0 ]; then
524513
+    error "${TEST}: we should not have any enabled (${enabled}) slots."
524513
+fi
524513
+
524513
+# Now we can restore it.
524513
+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
524513
+    error "${TEST}: error restoring ${DEV}."
524513
+fi
524513
+
524513
+# And compare whether they are the same.
524513
+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then
524513
+    error "${TEST}: the device differs from the original one after the restore."
524513
+fi
524513
+
524513
+# And making sure both the default passphrase and the binding work.
524513
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: the default passphrase for ${DEV} did no work."
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/backup-restore-luks2 b/src/luks/tests/backup-restore-luks2
524513
new file mode 100755
524513
index 0000000..a3b8608
524513
--- /dev/null
524513
+++ b/src/luks/tests/backup-restore-luks2
524513
@@ -0,0 +1,115 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+. clevis-luks-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS2.
524513
+DEV="${TMP}/luks2-device"
524513
+new_device "luks2" "${DEV}"
524513
+
524513
+SLT=5
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+ORIG_DEV="${TMP}/device.orig"
524513
+# Let's save it for comparison later.
524513
+cp -f "${DEV}" "${ORIG_DEV}"
524513
+
524513
+# Now let's backup and restore.
524513
+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then
524513
+    error "${TEST}: backup of ${DEV} failed."
524513
+fi
524513
+
524513
+# Now let's remove both the binding and the initial passphrase.
524513
+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: unbind of slot ${SLT} in ${DEV} failed."
524513
+fi
524513
+
524513
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: error removing the default password from ${DEV}."
524513
+fi
524513
+
524513
+# Making sure we have no slots enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" \
524513
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
524513
+if [ "${enabled}" -ne 0 ]; then
524513
+    error "${TEST}: we should not have any enabled (${enabled}) slots."
524513
+fi
524513
+
524513
+# Now we can restore it.
524513
+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then
524513
+    error "${TEST}: error restoring ${DEV}."
524513
+fi
524513
+
524513
+# And compare whether they are the same.
524513
+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then
524513
+    error "${TEST}: the device differs from the original one after the restore."
524513
+fi
524513
+
524513
+# And making sure both the default passphrase and the binding work.
524513
+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: the default passphrase for ${DEV} did no work."
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build
524513
index 248d2ea..4e0a6cb 100644
524513
--- a/src/luks/tests/meson.build
524513
+++ b/src/luks/tests/meson.build
524513
@@ -35,6 +35,9 @@ else
524513
   warning('Will not run "clevis luks list" tests due to missing jq dependency')
524513
 endif
524513
 test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env)
524513
+test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env)
524513
+test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90)
524513
+test('regen-not-inplace-luks1', find_program('regen-not-inplace-luks1'), env: env, timeout: 90)
524513
 
524513
 # LUKS2 tests go here, and they get included if we get support for it, based
524513
 # on the cryptsetup version.
524513
@@ -45,3 +48,7 @@ if jq.found()
524513
   test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60)
524513
 endif
524513
 test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60)
524513
+test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90)
524513
+test('regen-inplace-luks2', find_program('regen-inplace-luks2'), env: env, timeout: 90)
524513
+test('regen-not-inplace-luks2', find_program('regen-not-inplace-luks2'), env: env, timeout: 90)
524513
+
524513
diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1
524513
new file mode 100755
524513
index 0000000..3a42ced
524513
--- /dev/null
524513
+++ b/src/luks/tests/regen-inplace-luks1
524513
@@ -0,0 +1,98 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS1.
524513
+DEV="${TMP}/luks1-device"
524513
+new_device "luks1" "${DEV}"
524513
+
524513
+SLT=1
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+# Now let's remove the initial passphrase.
524513
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: error removing the default password from ${DEV}."
524513
+fi
524513
+
524513
+# Making sure we have a single slot enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
524513
+if [ "${enabled}" -ne 1 ]; then
524513
+    error "${TEST}: we should have only one slot enabled (${enabled})."
524513
+fi
524513
+
524513
+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
524513
+
524513
+# Now let's try regen.
524513
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: clevis luks regen failed"
524513
+fi
524513
+
524513
+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
524513
+
524513
+if [ "${old_key}" = "${new_key}" ]; then
524513
+    error "${TEST}: the passphrases should be different"
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2
524513
new file mode 100755
524513
index 0000000..1cb7a29
524513
--- /dev/null
524513
+++ b/src/luks/tests/regen-inplace-luks2
524513
@@ -0,0 +1,99 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS2.
524513
+DEV="${TMP}/luks2-device"
524513
+new_device "luks2" "${DEV}"
524513
+
524513
+SLT=1
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+# Now let's remove the initial passphrase.
524513
+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: error removing the default password from ${DEV}."
524513
+fi
524513
+
524513
+# Making sure we have a single slot enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" \
524513
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
524513
+if [ "${enabled}" -ne 1 ]; then
524513
+    error "${TEST}: we should have only one slot enabled (${enabled})."
524513
+fi
524513
+
524513
+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
524513
+
524513
+# Now let's try regen.
524513
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: clevis luks regen failed"
524513
+fi
524513
+
524513
+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}")
524513
+
524513
+if [ "${old_key}" = "${new_key}" ]; then
524513
+    error "${TEST}: the passphrases should be different"
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1
524513
new file mode 100755
524513
index 0000000..1b65ca7
524513
--- /dev/null
524513
+++ b/src/luks/tests/regen-not-inplace-luks1
524513
@@ -0,0 +1,95 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS1.
524513
+DEV="${TMP}/luks1-device"
524513
+new_device "luks1" "${DEV}"
524513
+
524513
+SLT=1
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+# Now let's rotate the keys in the server and remove the old ones, so that we
524513
+# will be unable to unlock the volume using clevis and will have to provide
524513
+# manually a password for clevis luks regen.
524513
+rm -rf "${TMP}"/db/*
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+fi
524513
+
524513
+# Making sure we have two slots enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l)
524513
+if [ "${enabled}" -ne 2 ]; then
524513
+    error "${TEST}: we should have two slots enabled (${enabled})."
524513
+fi
524513
+
524513
+# Now let's try regen.
524513
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: clevis luks regen failed"
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2
524513
new file mode 100755
524513
index 0000000..dc91449
524513
--- /dev/null
524513
+++ b/src/luks/tests/regen-not-inplace-luks2
524513
@@ -0,0 +1,96 @@
524513
+#!/bin/bash -x
524513
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
524513
+#
524513
+# Copyright (c) 2020 Red Hat, Inc.
524513
+# Author: Sergio Correia <scorreia@redhat.com>
524513
+#
524513
+# This program is free software: you can redistribute it and/or modify
524513
+# it under the terms of the GNU General Public License as published by
524513
+# the Free Software Foundation, either version 3 of the License, or
524513
+# (at your option) any later version.
524513
+#
524513
+# This program is distributed in the hope that it will be useful,
524513
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
524513
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
524513
+# GNU General Public License for more details.
524513
+#
524513
+# You should have received a copy of the GNU General Public License
524513
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
524513
+#
524513
+
524513
+TEST="${0}"
524513
+. tests-common-functions
524513
+
524513
+function on_exit() {
524513
+    if [ "$PID" ]; then kill $PID; wait $PID || true; fi
524513
+    [ -d "$TMP" ] && rm -rf $TMP
524513
+}
524513
+
524513
+trap 'on_exit' EXIT
524513
+trap 'exit' ERR
524513
+
524513
+export TMP=$(mktemp -d)
524513
+mkdir -p "${TMP}/db"
524513
+
524513
+# Generate the server keys
524513
+KEYS="$TMP/db"
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+    KEYS="${TMP}/cache"
524513
+fi
524513
+
524513
+# Start the server.
524513
+port=$(shuf -i 1024-65536 -n 1)
524513
+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" &
524513
+export PID=$!
524513
+sleep 0.25
524513
+
524513
+url="http://localhost:${port}"
524513
+adv="${TMP}/adv"
524513
+curl "${url}/adv" -o "${adv}"
524513
+
524513
+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")
524513
+
524513
+# LUKS2.
524513
+DEV="${TMP}/luks2-device"
524513
+new_device "luks2" "${DEV}"
524513
+
524513
+SLT=1
524513
+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: Bind should have succeeded."
524513
+fi
524513
+
524513
+# Now let's rotate the keys in the server and remove the old ones, so that we
524513
+# will be unable to unlock the volume using clevis and will have to provide
524513
+# manually a password for clevis luks regen.
524513
+rm -rf "${TMP}"/db/*
524513
+tangd-keygen $TMP/db sig exc
524513
+if which tangd-update; then
524513
+    mkdir -p "${TMP}/cache"
524513
+    tangd-update "${TMP}/db" "${TMP}/cache"
524513
+fi
524513
+
524513
+# Making sure we have two slots enabled.
524513
+enabled=$(cryptsetup luksDump "${DEV}" \
524513
+          | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l)
524513
+if [ "${enabled}" -ne 2 ]; then
524513
+    error "${TEST}: we should have two slots enabled (${enabled})."
524513
+fi
524513
+
524513
+# Now let's try regen.
524513
+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then
524513
+    error "${TEST}: clevis luks regen failed"
524513
+fi
524513
+
524513
+TEST_DEV="test-device-${RANDOM}"
524513
+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then
524513
+    error "${TEST}: we were unable to unlock ${DEV}."
524513
+fi
524513
+
524513
+cryptsetup close "${TEST_DEV}"
524513
+
524513
+kill -9 "${PID}"
524513
+! wait "${PID}"
524513
+unset PID
524513
diff --git a/src/luks/tests/tests-common-functions b/src/luks/tests/tests-common-functions
524513
index 7758876..1139f09 100644
524513
--- a/src/luks/tests/tests-common-functions
524513
+++ b/src/luks/tests/tests-common-functions
524513
@@ -63,7 +63,7 @@ new_device() {
524513
         return 0
524513
     fi
524513
 
524513
-    fallocate -l16M "${DEV}"
524513
+    fallocate -l64M "${DEV}"
524513
     local extra_options='--pbkdf pbkdf2 --pbkdf-force-iterations 1000'
524513
     cryptsetup luksFormat --type "${LUKS}" ${extra_options} --batch-mode --force-password "${DEV}" <<< "${DEFAULT_PASS}"
524513
     # Caching the just-formatted device for possible reuse.
524513
@@ -83,4 +83,29 @@ pin_cfg_equal() {
524513
          <(jq -S . < <(echo -n "${cfg2}"))
524513
 }
524513
 
524513
+clevis_regen() {
524513
+    local DEV="${1}"
524513
+    local SLT="${2}"
524513
+    local PASS="${3}"
524513
+
524513
+    expect -d << CLEVIS_REGEN
524513
+        set timeout 120
524513
+        spawn sh -c "clevis luks regen -d $DEV -s $SLT"
524513
+        expect {
524513
+            "Enter existing LUKS password" {
524513
+                send "$PASS\r"
524513
+                exp_continue
524513
+            }
524513
+            "Do you wish to trust these keys" {
524513
+                send "y\r"
524513
+                exp_continue
524513
+            }
524513
+            expect eof
524513
+            wait
524513
+        }
524513
+CLEVIS_REGEN
524513
+    ret=$?
524513
+    return "${ret}"
524513
+}
524513
+
524513
 export DEFAULT_PASS='just-some-test-password-here'
524513
-- 
524513
2.18.2
524513