Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fde-tools for openSUSE:Factory 
checked in at 2026-09-23 14:34:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fde-tools (Old)
 and      /work/SRC/openSUSE:Factory/.fde-tools.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fde-tools"

Wed Sep 23 14:34:13 2026 rev:38 rq:1379413 version:0.7.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/fde-tools/fde-tools.changes      2026-08-12 
16:10:59.926657927 +0200
+++ /work/SRC/openSUSE:Factory/.fde-tools.new.383539/fde-tools.changes  
2026-09-23 14:35:33.428851049 +0200
@@ -1,0 +2,11 @@
+Mon Sep 21 07:03:14 UTC 2026 - Gary Ching-Pang Lin <[email protected]>
+
+- Update to 0.7.9
+  + Handle the return value of pcr-oracle when signing the key
+    (bsc#1279917)
+  + Properly detect the sealed key in tpm-activate/tpm-enable
+    (bsc#1279917)
+  + search for fde password also in EFI grub.cfg
+  + ensure keyfile is not dropped if reencryption failed
+
+-------------------------------------------------------------------

Old:
----
  fde-tools-0.7.7.tar.bz2

New:
----
  fde-tools-0.7.9.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fde-tools.spec ++++++
--- /var/tmp/diff_new_pack.Gjy6Pk/_old  2026-09-23 14:35:34.046876604 +0200
+++ /var/tmp/diff_new_pack.Gjy6Pk/_new  2026-09-23 14:35:34.048876687 +0200
@@ -21,7 +21,7 @@
 %endif
 
 Name:           fde-tools
-Version:        0.7.7
+Version:        0.7.9
 Release:        0
 Summary:        Tools required for Full Disk Encryption
 License:        GPL-2.0-only

++++++ fde-tools-0.7.7.tar.bz2 -> fde-tools-0.7.9.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/fde.sh new/fde-tools-0.7.9/fde.sh
--- old/fde-tools-0.7.7/fde.sh  2026-08-11 09:15:24.667343794 +0200
+++ new/fde-tools-0.7.9/fde.sh  2026-09-21 09:01:35.111879156 +0200
@@ -22,7 +22,7 @@
 
 : ${SHAREDIR:=/usr/share/fde}
 
-version=0.7.7
+version=0.7.9
 
 opt_bootloader=grub2
 opt_uefi_bootdir=""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/firstboot/fde 
new/fde-tools-0.7.9/firstboot/fde
--- old/fde-tools-0.7.7/firstboot/fde   2025-07-29 05:00:40.734993675 +0200
+++ new/fde-tools-0.7.9/firstboot/fde   2026-09-21 08:57:10.277341262 +0200
@@ -52,6 +52,10 @@
     grub_get_fde_password "$@"
 }
 
+function bootloader_get_keyslots {
+    grub_get_keyslots "$@"
+}
+
 function bootloader_platform_parameters {
    grub_platform_parameters
 }
@@ -87,6 +91,10 @@
     with_tpm=false
     with_ccid=false
 
+    # Set when the user asked for a recovery password but we could not install
+    # it. The key slots have to be left alone in that case.
+    pass_failed=false
+
     is_reencrypted=false
 
     for method in $FDE_PROTECTION; do
@@ -134,47 +142,78 @@
 
     luks_current_password="${luks_recovery_pass}"
 
-    # Check if the installer/imager has created a secondary slot that is 
protected
-    # by a random key.
-    # "cryptsetup reencrypt" doesn't really deal all that well with a LUKS
-    # header that has more than one valid key slot. To avoid any ugly 
gymnastics,
-    # simply drop that slot.
-    if [ -n "$luks_keyfile" ]; then
-       # Skip luks_drop_key if the partition is already reencrypted
-       if [ "$is_reencrypted" == "false" ]; then
-           if ! luks_drop_key "${luks_dev}" "${luks_keyfile}"; then
-               display_errorbox "Failed to remove initial random key"
-               return 1
-           fi
-       fi
-
-       rm -f "${luks_keyfile}"
-
-       # Remove the dracut conf for the key file
-       rm -f /etc/dracut.conf.d/99-luks-boot.conf
-
-       # Replace the key file path in /etc/crypttab with "/.virtual-root.key"
-       # to avoid errors when unmounting the LUKS partition (bsc#1218181)
-       sed -i "s,${luks_keyfile},/.virtual-root.key,g" /etc/crypttab
-
-       luks_keyfile=""
+    # The installer/imager may have created a secondary slot that is protected
+    # by a random key, stored in ${luks_keyfile}. That key is a far better
+    # credential than the built-in recovery password: we can check that it is
+    # valid, whereas the recovery password is only a guess, and in FIPS mode
+    # PBKDF2 refuses passphrases shorter than 8 characters, so a short built-in
+    # password fails to open any key slot at all.
+    # Do not drop the slot here: it is the only credential we are sure about,
+    # and both the recovery path below and fde_setup_unencrypted still need it.
+    luks_imager_keyfile=""
+    if [ -n "$luks_keyfile" ] && luks_verify_password "${luks_dev}" 
"${luks_keyfile}"; then
+       luks_imager_keyfile="${luks_keyfile}"
     fi
 
     # Change the built-in recovery password to the one provided by the user
     # FIXME: only do this if the password is well-known, eg when dealing with 
a VM image
     # shipped by us.
     if $with_pass; then
-       if luks_change_password "${luks_dev}" "${luks_current_password}"; then
+       if luks_change_password "${luks_dev}" "${luks_current_password}" 
"${luks_imager_keyfile}"; then
            luks_current_password="${result_password}"
        else
            display_errorbox "Failed to change recovery password."
            with_pass=false
+           pass_failed=true
        fi
     fi
 
     # Write the current password to a file for the later operations
     pass_keyfile=$(luks_write_password pass "${luks_current_password}")
 
+    # Now that the recovery password is in place, drop every key slot we no
+    # longer need: the imager's random key, and the slot that still holds the
+    # built-in recovery password when luks_change_password authenticated with
+    # the imager key rather than with that password. TPM protected slots are
+    # preserved.
+    # "cryptsetup reencrypt" doesn't really deal all that well with a LUKS
+    # header that has more than one valid key slot, so this has to happen
+    # before luks_reencrypt below.
+    # Leave the header alone if we failed to install the recovery password:
+    # dropping slots at that point would only throw away the last credential
+    # the recovery path below can still use.
+    drop_imager_keyfile=false
+    if $with_pass; then
+       if ! luks_drop_other_keyslots "${luks_dev}" "${pass_keyfile}"; then
+           display_errorbox "Failed to remove initial random key"
+           return 1
+       fi
+       drop_imager_keyfile=true
+    elif ! $pass_failed && [ -n "$luks_imager_keyfile" ]; then
+       # No recovery password was requested, so there is no obsolete slot to
+       # collect either; just drop the imager's random key as we always did.
+       if ! luks_drop_key "${luks_dev}" "${luks_imager_keyfile}"; then
+           display_errorbox "Failed to remove initial random key"
+           return 1
+       fi
+       drop_imager_keyfile=true
+    fi
+
+    if $drop_imager_keyfile && [ -n "$luks_keyfile" ]; then
+       rm -f "${luks_keyfile}"
+
+       # Remove the dracut conf for the key file
+       rm -f /etc/dracut.conf.d/99-luks-boot.conf
+
+       # Replace the key file path in /etc/crypttab with
+       # "/.virtual-root.key" to avoid errors when unmounting the LUKS
+       # partition (bsc#1218181). The path must not be on / itself, which is
+       # read-only on SL Micro.
+       sed -i "s,${luks_keyfile},/.virtual-root.key,g" /etc/crypttab
+
+       luks_keyfile=""
+    fi
+
     # Reencrypt with the new password
     # FIXME: only do this if the LUKS master key is well-known, eg when 
dealing with
     # a VM image.
@@ -236,7 +275,18 @@
        return 1
     fi
 
-    luks_decrypt "${luks_dev}" "${luks_keyfile}"
+    # We may be called to recover from a failed fde_setup_encrypted, which
+    # drops the imager key file once it is no longer needed. Fall back to the
+    # recovery password in that case.
+    if [ ! -s "${luks_keyfile}" ]; then
+       luks_keyfile=$(luks_write_password recovery "${luks_recovery_pass}")
+    fi
+
+    if ! luks_decrypt "${luks_dev}" "${luks_keyfile}"; then
+       rm -f "${luks_keyfile}"
+       display_errorbox "Failed to decrypt ${luks_dev}"
+       return 1
+    fi
 
     rm -f "${luks_keyfile}"
     rm -f /etc/crypttab
@@ -322,6 +372,14 @@
 
 function fde_firstboot {
 
+    # fde_setup_encrypted keeps these two up to date as it goes: it consumes
+    # the imager key file once the recovery password is in place, and replaces
+    # the built-in recovery password with the one chosen by the user. The
+    # recovery path below has to use whatever is valid by then, not the
+    # credentials we were originally called with.
+    luks_keyfile="$2"
+    luks_current_password="$3"
+
     if fde_choose_protection; then
        if fde_setup_encrypted "$@"; then
            return 0
@@ -330,7 +388,7 @@
        display_errorbox "Failed to set up for full disk encryption. Trying to 
recover"
     fi
 
-    fde_setup_unencrypted "$@"
+    fde_setup_unencrypted "$1" "${luks_keyfile}" "${luks_current_password}"
 }
 
 function fde_systemd_firstboot {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/share/commands/tpm-activate 
new/fde-tools-0.7.9/share/commands/tpm-activate
--- old/fde-tools-0.7.7/share/commands/tpm-activate     2025-07-29 
05:00:40.734993675 +0200
+++ new/fde-tools-0.7.9/share/commands/tpm-activate     2026-09-21 
08:57:08.235352536 +0200
@@ -26,7 +26,11 @@
 
     local luks_devices="$1"
 
-    if bootloader_check_sealed_key; then
+    # A sealed key alone is not enough to skip activation: the installer
+    # may have left a firstboot password behind, and it is only good for
+    # one reboot. tpm_enable removes it, so let it run in that case.
+    if bootloader_check_sealed_key && \
+       [ -z "$(bootloader_get_fde_password)" ]; then
         fde_trace "LUKS key already sealed. Skip activation."
         return 0
     fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/share/commands/tpm-enable 
new/fde-tools-0.7.9/share/commands/tpm-enable
--- old/fde-tools-0.7.7/share/commands/tpm-enable       2025-07-29 
05:00:40.734993675 +0200
+++ new/fde-tools-0.7.9/share/commands/tpm-enable       2026-09-21 
08:57:08.236352531 +0200
@@ -28,6 +28,18 @@
 
     st=1
 
+    # Remember whether a firstboot password was set: the removal below
+    # clears it, so the check further down can no longer detect it.
+    local had_fde_password=
+    if [ -n "$(bootloader_get_fde_password)" ]; then
+       had_fde_password=y
+    fi
+
+    # Ensure the sealed key is usable before removing firstboot password.
+    if ! tpm_enable_check_sealed_key; then
+       return 1
+    fi
+
     # The installer may have set a random password to avoid the password
     # prompt on firstboot.
     # We must clear this before computing any PCR policies, otherwise
@@ -66,9 +78,11 @@
        # systemd). Obviously, there was no key to enroll, so we might
        # as well return.
        #
-       # However, if there was a firstboot password, fall thru to remove the 
password.
+       # However, if there was a firstboot password, fall thru to commit
+       # the configuration, so that the password is dropped from the
+       # boot loader config in the ESP too.
        # After all, it's supposed to be good for one reboot only.
-       if [ "$(bootloader_get_fde_password)" = "" ]; then
+       if [ -z "$had_fde_password" ]; then
            return 0
        fi
        st=0
@@ -82,6 +96,35 @@
     return $st
 }
 
+# Verify that the sealed key can be loaded and get its SRK algorithm.
+function tpm_enable_check_sealed_key {
+
+    if [[ ! "$FDE_USE_AUTHORIZED_POLICIES" =~ y.* ]] ||
+       [ -z "$FDE_AUTHORIZED_POLICY" ]; then
+       return 0
+    fi
+
+    # Older pcr-oracle versions cannot answer this, skip check.
+    if ! tpm_have_load_test; then
+       fde_trace "Unable to determine the SRK of the sealed key"
+       return 0
+    fi
+
+    tpm_define_authorized_policy_paths "$FDE_AUTHORIZED_POLICY"
+
+    if [ ! -f "$FDE_AP_SEALED_SECRET" ]; then
+       # Handled by tpm_enable_authorized_policy.
+       return 0
+    fi
+
+    if tpm_get_srk_alg "$FDE_AP_SEALED_SECRET" >/dev/null; then
+       return 0
+    fi
+
+    display_errorbox "The sealed key in $FDE_AP_SEALED_SECRET cannot be loaded 
by this TPM. Refusing to update the boot loader configuration, as the system 
would no longer boot unattended. Use \"fdectl regenerate-key\" to seal a new 
key."
+    return 1
+}
+
 function tpm_enable_authorized_policy {
 
     local luks_devices="$1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/share/grub2 
new/fde-tools-0.7.9/share/grub2
--- old/fde-tools-0.7.7/share/grub2     2026-08-11 09:15:17.915379638 +0200
+++ new/fde-tools-0.7.9/share/grub2     2026-09-21 08:57:10.278341256 +0200
@@ -68,11 +68,48 @@
 
     . /etc/default/grub
 
-    if [ -z "$GRUB_CRYPTODISK_PASSWORD" ]; then
+    if [ -n "$GRUB_CRYPTODISK_PASSWORD" ]; then
+       echo "$GRUB_CRYPTODISK_PASSWORD"
+       return 0
+    fi
+
+    # Fallback: the image build may have embedded the well-known
+    # passphrase directly in the EFI grub.cfg via 'cryptomount -p "..."'
+    # instead of GRUB_CRYPTODISK_PASSWORD in /etc/default/grub.
+    local pass
+    [ -f /boot/efi/EFI/BOOT/grub.cfg ] || return 1
+    pass=$(sed -n 
's/.*cryptomount[[:space:]].*-p[[:space:]]*"\([^"]*\)".*/\1/p' 
/boot/efi/EFI/BOOT/grub.cfg | head -n1)
+    if [ -n "$pass" ]; then
+       echo "$pass"
+       return 0
+    fi
+
+    return 1
+}
+
+# Get the configured SRK algorithm from grub.
+function grub_get_srk_alg {
+
+    if [ -f /etc/default/grub ]; then
+       . /etc/default/grub
+    fi
+
+    echo "$GRUB_TPM2_SRK_ALG"
+}
+
+##################################################################
+# Print the path of the sealed key the boot loader reads.
+##################################################################
+function grub_get_sealed_key_path {
+
+    local grub_efi_dir
+    grub_efi_dir=$(uefi_get_current_efidir)
+
+    if [ -z "$grub_efi_dir" ]; then
        return 1
     fi
 
-    echo "$GRUB_CRYPTODISK_PASSWORD"
+    echo "$grub_efi_dir/sealed.tpm"
 }
 
 ##################################################################
@@ -82,13 +119,14 @@
 function grub_update_early_config {
 
     local sealed_key_file="$1"
+    local srk_alg="$2"
 
     grub_set_control GRUB_ENABLE_CRYPTODISK "y"
     grub_set_control GRUB_TPM2_SEALED_KEY "$sealed_key_file"
-    if [ "$(tpm_get_ecc_srk_support)" = "yes" ]; then
-       grub_set_control GRUB_TPM2_SRK_ALG "ECC"
-    else
-       grub_set_control GRUB_TPM2_SRK_ALG "RSA"
+
+    # Keep current SRK setting if caller cannot determine it.
+    if [ -n "$srk_alg" ]; then
+       grub_set_control GRUB_TPM2_SRK_ALG "$srk_alg"
     fi
 
     # Do not clear the password implicitly; require fdectl or
@@ -113,8 +151,37 @@
 
 function grub_enable_fde_authorized_policy {
 
-    # Set up grub.cfg
-    grub_update_early_config sealed.tpm
+    local sealed_key_file="$1"
+
+    # Retain the key's existing SRK.
+    grub_update_early_config sealed.tpm "$(tpm_get_srk_alg "$sealed_key_file")"
+}
+
+##################################################################
+# Align configured SRK algorithm with the key's actual SRK.
+##################################################################
+function grub_repair_srk_alg {
+
+    local sealed_key_file="$1"
+    local srk_alg
+    local config_alg
+
+    srk_alg=$(tpm_get_srk_alg "$sealed_key_file") || return 0
+
+    config_alg=$(grub_get_srk_alg)
+    if [ "$srk_alg" = "$config_alg" ]; then
+       return 0
+    fi
+
+    fde_trace "Boot loader configured for a ${config_alg:-unset} SRK, but the 
sealed key uses ${srk_alg}; updating the configuration"
+
+    grub_set_control GRUB_TPM2_SRK_ALG "$srk_alg"
+
+    # Commit config before signing.
+    if ! grub_commit_config; then
+       fde_trace "Failed to update the boot loader configuration"
+       return 1
+    fi
 }
 
 function grub_authorize_pcr_policy {
@@ -122,8 +189,10 @@
     private_key_file="$1"
     sealed_key_file="$2"
 
-    grub_efi_dir=$(uefi_get_current_efidir)
-    if [ -z "$grub_efi_dir" ]; then
+    local grub_sealed_key
+    grub_sealed_key=$(grub_get_sealed_key_path) || return 1
+
+    if ! grub_repair_srk_alg "$sealed_key_file"; then
        return 1
     fi
 
@@ -133,23 +202,21 @@
     # append the valid signatures into the key file.
 
     tpm_authorize "$private_key_file" "$sealed_key_file" \
-                 "$grub_efi_dir/sealed.tpm"
+                 "$grub_sealed_key"
 }
 
 function grub_enable_fde_pcr_policy {
 
     luks_keyfile="$1"
 
-    grub_efi_dir=$(uefi_get_current_efidir)
-    if [ -z "$grub_efi_dir" ]; then
-       return 1
-    fi
+    local grub_sealed_key
+    grub_sealed_key=$(grub_get_sealed_key_path) || return 1
 
     # First update grub.cfg...
-    grub_update_early_config sealed.tpm
+    grub_update_early_config sealed.tpm "$(tpm_get_new_srk_alg)"
 
     # ... then seal the key against a PCR9 value that covers grub.cfg
-    tpm_seal_secret "${luks_keyfile}" "$grub_efi_dir/sealed.tpm"
+    tpm_seal_secret "${luks_keyfile}" "$grub_sealed_key"
 }
 
 function grub_enable_fde_without_tpm {
@@ -166,26 +233,54 @@
 
 function grub_check_sealed_key {
 
-    grub_efi_dir=$(uefi_get_current_efidir)
-    if [ -z "$grub_efi_dir" ]; then
+    local sealed_key
+    sealed_key=$(grub_get_sealed_key_path) || return 1
+
+    if [ ! -f "${sealed_key}" ]; then
        return 1
     fi
 
-    if [ -f "$grub_efi_dir/sealed.tpm" ]; then
-        return 0
+    # Without authorized policies there is no base key to compare
+    # against: the key in the EFI partition is the key.
+    if [[ ! "$FDE_USE_AUTHORIZED_POLICIES" =~ y.* ]]; then
+       return 0
     fi
 
-    return 1
+    # With authorized policies, the signed key in the EFI partition is
+    # only valid if it is derived from the sealed base key. Anything that
+    # prevents the comparison leaves a stale key indistinguishable from a
+    # good one, so report the key as not sealed and let tpm_enable emit a
+    # proper error.
+    if [ -z "${FDE_AUTHORIZED_POLICY}" ]; then
+       fde_trace "Authorized policy not created yet: ESP key is stale."
+       return 1
+    fi
+
+    # The paths are defined here since this function may run before any
+    # caller of tpm_set_authorized_policy_paths.
+    tpm_define_authorized_policy_paths "${FDE_AUTHORIZED_POLICY}"
+
+    if [ ! -f "${FDE_AP_SEALED_SECRET}" ]; then
+       fde_trace "Base key not found: the sealed key in the ESP is stale."
+       return 1
+    fi
+
+    if ! type tpm2key-tool >/dev/null 2>&1; then
+       fde_trace "tpm2key-tool not available: cannot verify the ESP key."
+       return 0
+    fi
+
+    # If both keys contain the same public and private keys, the signed
+    # key is derived from the base key.
+    tpm2key-tool cmp "${sealed_key}" "${FDE_AP_SEALED_SECRET}" >/dev/null 2>&1
+    return $?
 }
 
 function grub_remove_sealed_key {
 
     # Remove the sealed key file in the EFI system partition
-    grub_efi_dir=$(uefi_get_current_efidir)
-    if [ -z "$grub_efi_dir" ]; then
-       return 1
-    fi
-    grub_sealed_key="$grub_efi_dir/sealed.tpm"
+    local grub_sealed_key
+    grub_sealed_key=$(grub_get_sealed_key_path) || return 1
 
     if [ -f "${grub_sealed_key}" ]; then
         rm -f "${grub_sealed_key}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/share/luks 
new/fde-tools-0.7.9/share/luks
--- old/fde-tools-0.7.7/share/luks      2026-08-11 09:15:17.915379638 +0200
+++ new/fde-tools-0.7.9/share/luks      2026-09-21 08:57:10.278341256 +0200
@@ -217,6 +217,96 @@
 }
 
 ##################################################################
+# List the active key slots of a LUKS device
+##################################################################
+function luks_list_keyslots {
+
+    local luks_dev=$1
+
+    LC_ALL=C cryptsetup luksDump "${luks_dev}" |
+       sed -n '/^Keyslots:/,/^Tokens:/s/^  \([0-9]\+\): luks2$/\1/p'
+}
+
+##################################################################
+# Report the key slot that the given key file unlocks
+##################################################################
+function luks_keyslot_for_keyfile {
+
+    local luks_dev=$1
+    local luks_keyfile="$2"
+
+    LC_ALL=C cryptsetup open --test-passphrase --verbose \
+               --key-file "${luks_keyfile}" "${luks_dev}" |
+       sed -En 's/^Key slot ([0-9]+) unlocked\.$/\1/p'
+}
+
+##################################################################
+# Drop every key slot of a LUKS device except the one unlocked by
+# the given key file and those protected by the TPM.
+#
+# The TPM key slots are identified through the bootloader specific
+# LUKS2 tokens, so that a system that is already enrolled does not
+# lose its TPM protection here.
+#
+# Note that "cryptsetup luksKillSlot" happily wipes the last
+# remaining key slot and does not validate --key-file, so we have
+# to make sure ourselves that we never wipe the slot we want to
+# keep.
+##################################################################
+function luks_drop_other_keyslots {
+
+    local luks_dev=$1
+    local luks_keyfile="$2"
+    local keep_slot
+    local tpm_slots
+    local slot
+
+    keep_slot=$(luks_keyslot_for_keyfile "${luks_dev}" "${luks_keyfile}")
+    if [ -z "${keep_slot}" ]; then
+       fde_trace "Unable to identify the key slot to preserve on ${luks_dev}"
+       return 1
+    fi
+    # A key file should unlock a single slot. If it opens more than one
+    # we cannot tell which to keep, so bail rather than guessing.
+    if [[ "${keep_slot}" == *$'\n'* ]]; then
+       fde_trace "Key file unlocks several slots on ${luks_dev}, cannot decide 
which to preserve: ${keep_slot}"
+       return 1
+    fi
+
+    # Bail out rather than wipe a TPM slot we merely failed to enumerate.
+    if ! tpm_slots=$(bootloader_get_keyslots "${luks_dev}"); then
+       fde_trace "Unable to list the TPM protected key slots of ${luks_dev}"
+       return 1
+    fi
+    # A TPM slot is one the bootloader reports. bootloader_get_keyslots
+    # (fdectl-grub-tpm2 list --key-only) prints the keyslot ids bare, one
+    # per line. We do not quote the list: "echo" without quotes collapses
+    # the newlines into single spaces so every id becomes " 12 ", and the
+    # space-bounded substring test keeps id 1 distinct from id 10.
+    tpm_slots=" $(echo ${tpm_slots}) "
+
+    for slot in $(luks_list_keyslots "${luks_dev}"); do
+       test "${slot}" != "${keep_slot}" || continue
+       if [[ "${tpm_slots}" == *" ${slot} "* ]]; then
+           fde_trace "Preserving TPM protected key slot ${slot}"
+           continue
+       fi
+
+       display_infobox "Dropping obsolete key slot ${slot} (${luks_dev})"
+       # Pass the key file even though luksKillSlot does not insist on one, so
+       # that it does not go looking for a passphrase on our stdin, which
+       # belongs to the dialog UI
+       if ! cryptsetup luksKillSlot -q --key-file "${luks_keyfile}" \
+                       "${luks_dev}" "${slot}" </dev/null; then
+           fde_trace "Warning: luksKillSlot ${slot} indicates failure"
+           return 1
+       fi
+    done
+
+    return 0
+}
+
+##################################################################
 # Verify an existing password
 ##################################################################
 function luks_verify_password {
@@ -254,13 +344,25 @@
 # Change an existing password
 # This function uses request_new_password to prompt the user for
 # the new password.
+#
+# The third argument is optional. When given, it names a key file
+# that is known to unlock the device, eg the random key left behind
+# by the imager, and it is used as the credential for luksChangeKey
+# instead of the old password. This matters because the old password
+# is often only a guess, and because PBKDF2 in FIPS mode rejects
+# passphrases shorter than 8 characters outright, so a short built-in
+# password cannot open any key slot at all.
+# Note that luksChangeKey rewrites the key slot it authenticated
+# with, so the caller is responsible for dropping the slot that still
+# holds the old password.
 ##################################################################
 function luks_change_password {
 
     local luks_dev=$1
     local luks_old_password="$2"
+    local luks_unlock_keyfile="$3"
 
-    if [ -z "$luks_old_password" ]; then
+    if [ -z "$luks_old_password" -a -z "$luks_unlock_keyfile" ]; then
        request_password "Please enter old LUKS recovery password"
         if [ -z "$result_password" ]; then
             fde_trace "Unable to obtain old recovery password"
@@ -275,14 +377,20 @@
        return 1
     fi
 
-    old_keyfile=$(luks_write_password oldpass "${luks_old_password}")
+    if [ -n "$luks_unlock_keyfile" ]; then
+       old_keyfile="${luks_unlock_keyfile}"
+    else
+       old_keyfile=$(luks_write_password oldpass "${luks_old_password}")
+    fi
     new_keyfile=$(luks_write_password newpass "${result_password}")
     if ! luks_set_password "${luks_dev}" "${old_keyfile}" "${new_keyfile}"; 
then
-       rm -f ${new_keyfile} ${old_keyfile}
+       rm -f ${new_keyfile}
+       test -n "$luks_unlock_keyfile" || rm -f ${old_keyfile}
        return 1
     fi
 
-    rm -f ${new_keyfile} ${old_keyfile}
+    rm -f ${new_keyfile}
+    test -n "$luks_unlock_keyfile" || rm -f ${old_keyfile}
 }
 
 function luks_add_password {
@@ -405,14 +513,32 @@
 
     luks_dev="$1"
     luks_keyfile="$2"
+    local status_file
+    local status
+
+    # The exit status of cryptsetup is swallowed by the pipeline feeding the
+    # gauge, so stash it in a file. Callers act on the result by removing
+    # /etc/crypttab, which must not happen when the device is still encrypted.
+    status_file=$(fde_make_tempfile decrypt.status)
 
     # Online reencryption works with LUKS2 only. If we ever want to do FDE 
with luks1,
     # we need to perform reencryption during installation, after dd'ing the 
image to
     # disk and prior to mounting it.
     {
-       LC_ALL=C cryptsetup reencrypt --decrypt --key-file "$luks_keyfile" 
--progress-frequency 1 $luks_dev 2>&1|
+       { LC_ALL=C cryptsetup reencrypt --decrypt --key-file "$luks_keyfile" 
--progress-frequency 1 $luks_dev 2>&1
+         echo $? >"$status_file"; } |
            sed -u 's/.* \([0-9]*\)[0-9.]*%.*/\1/'
            echo 100
     } | display_gauge "Decrypting LUKS device $luks_dev"
+
+    status=$(cat "$status_file" 2>/dev/null)
+    rm -f "$status_file"
+
+    if [ "$status" != "0" ]; then
+       fde_trace "Warning: cryptsetup reencrypt --decrypt indicates failure"
+       return 1
+    fi
+
+    return 0
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fde-tools-0.7.7/share/tpm 
new/fde-tools-0.7.9/share/tpm
--- old/fde-tools-0.7.7/share/tpm       2026-08-11 09:15:17.915379638 +0200
+++ new/fde-tools-0.7.9/share/tpm       2026-09-21 08:57:08.236352531 +0200
@@ -125,6 +125,61 @@
     echo "$__fde_ecc_srk"
 }
 
+##################################################################
+# Check if pcr-oracle supports load-test.
+##################################################################
+function tpm_have_load_test {
+
+    declare -g __fde_have_load_test
+
+    if [ -z "$__fde_have_load_test" ]; then
+       if LC_ALL=C pcr-oracle load-test 2>&1 | grep -q "Unknown action"; then
+           __fde_have_load_test="no"
+       else
+           __fde_have_load_test="yes"
+       fi
+    fi
+
+    [ "$__fde_have_load_test" = "yes" ]
+}
+
+##################################################################
+# Get the SRK algorithm of an existing sealed key.
+##################################################################
+function tpm_get_srk_alg {
+
+    local sealed_key="$1"
+
+    declare -g __fde_srk_alg
+
+    if [ -z "$__fde_srk_alg" ]; then
+       if [ ! -f "$sealed_key" ] || ! tpm_have_load_test; then
+           return 1
+       fi
+
+       __fde_srk_alg=$(pcr-oracle --input "$sealed_key" load-test 2>/dev/null)
+       if [ -z "$__fde_srk_alg" ]; then
+           fde_trace "${sealed_key} cannot be loaded under any known SRK"
+           return 1
+       fi
+    fi
+
+    echo "$__fde_srk_alg"
+}
+
+##################################################################
+# Get the expected SRK algorithm for a newly sealed key.
+##################################################################
+function tpm_get_new_srk_alg {
+
+    if [ "$(tpm_get_ecc_srk_support)" = "yes" ]; then
+       echo "ECC"
+       return
+    fi
+
+    echo "RSA2048"
+}
+
 function tpm_snapshot {
     # TODO Add an ID to the snapshot name
     local snapshot=${FDE_SNAPSHOT_NAME}
@@ -323,8 +378,11 @@
                        --after \
                        seal-secret \
                        "$FDE_SEAL_PCR_LIST"
+    local rc=$?
 
     tpm_snapshot
+
+    return $rc
 }
 
 function tpm_test {
@@ -436,7 +494,9 @@
 ##################################################################
 # Authorized policy support
 ##################################################################
-function tpm_set_authorized_policy_paths {
+# Define the authorized policy paths without touching the file system,
+# so that read-only callers can look the files up.
+function tpm_define_authorized_policy_paths {
 
     policy_name="$1"
 
@@ -447,6 +507,11 @@
     declare -g FDE_AP_AUTHPOLICY="$FDE_AP_CONFIG_DIR/authorized-policy.tpm"
     declare -g FDE_AP_PUBLIC_KEY="$FDE_AP_CONFIG_DIR/public-key.tpm"
     declare -g FDE_AP_SEALED_SECRET="$FDE_AP_CONFIG_DIR/sealed.tpm"
+}
+
+function tpm_set_authorized_policy_paths {
+
+    tpm_define_authorized_policy_paths "$1"
 
     mkdir -p -m 755 "$FDE_AP_CONFIG_DIR"
 }
@@ -498,6 +563,14 @@
     local extra_opts=$(tpm_platform_parameters)
     local stop_event=$(bootloader_stop_event)
 
+    # pcr-oracle writes straight to its --output, which is the key the
+    # boot loader is currently using. Sign into a sibling file and rename
+    # it over the live one only on success, so that a failed signature
+    # leaves the previous working key in place. The staged file must be a
+    # sibling to stay on the same file system, otherwise the rename falls
+    # back to a copy and may leave a partial key behind.
+    local staged_key_file="${signed_key_file}.new"
+
     pcr-oracle ${extra_opts} \
                --algorithm "$FDE_SEAL_PCR_BANK" \
                 --private-key "$private_key_file" \
@@ -505,8 +578,18 @@
                --stop-event "$stop_event" \
                --after \
                --input "$sealed_key_file" \
-                --output "$signed_key_file" \
+                --output "$staged_key_file" \
                 sign "$FDE_SEAL_PCR_LIST"
+    local rc=$?
+
+    if [ $rc -eq 0 ]; then
+       mv -f "$staged_key_file" "$signed_key_file"
+       rc=$?
+    else
+       rm -f "$staged_key_file"
+    fi
 
     tpm_snapshot
+
+    return $rc
 }

Reply via email to