Re: [PATCH v2 4/4] initramfs-crypt-hook: add re-encryption recovery

Quirin Gylstorff
2025-02-27T14:57:00Z

On 2/27/25 15:46, Claudius Heine wrote:
> Hi Quirin,
>
> On 2025-02-27 3:37 pm, Quirin Gylstorff wrote:
>>
>>
>> On 2/27/25 15:30, Claudius Heine wrote:
>>> Integrate detection and recovery of power failures while a partition is
>>> being encrypted.
>>>
>>> There are possible scenarios:
>>> 1. Power-fail happens while the partition is reencrypted:
>>>    - The LUKS header contains `online-reencrypt-v2` and needs to be
>>>      repaired with `cryptsetup repair` before it can continue.
>>>    - Also no resizing of the file system is necessary
>>> 2. Power-fail happens before the systemd-tpm2 token can be installed
>>>    - The LUKS header does not contain 'systemd-tpm2', thus it needs
>>> to be
>>>      registered and the temporary encryption key needs to be removed
>>>
>>> In both scenarios the system after the reboot needs to have access to
>>> the temporary encryption key that was initially used. So using a random
>>> one, generated via `openssl rand` is not possible. Since it is only a
>>> temporary key and gets removed after the systemd-tpm2 token was
>>> installed, a known password can be used.
>>>
>>> The list of these scenarios is not complete, there might be other
>>> instances where a sudden power-fail could be fatal to the system, but
>>> these where the most obvious and risky ones.
>>>
>>> Signed-off-by: Claudius Heine <ch@denx.de>
>>> ---
>>>   .../files/local-top-complete                  | 33 +++++++++++++++----
>>>   .../initramfs-crypt-hook_0.6.bb               |  5 ++-
>>>   2 files changed, 29 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/recipes-initramfs/initramfs-crypt-hook/files/local-top-
>>> complete b/recipes-initramfs/initramfs-crypt-hook/files/local-top-
>>> complete
>>> index 4b6451a..6034175 100644
>>> --- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
>>> +++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
>>> @@ -72,6 +72,9 @@ reencrypt_existing_partition() {
>>>       reduced_size="$(expr "$part_size_blocks" - 65536 )"
>>>       reduced_size_in_byte="$(expr "$reduced_size" \* 512)"
>>>       reduced_size_in_kb="$(expr "$reduced_size_in_byte" / 1024)K"
>>> +
>>> +    CRYPTSETUP_PARAMS="--reduce-device-size ${reduce_device_size}k"
>>> +
>>>       case $partition_fstype in
>>>       ext*)
>>>           # reduce the filesystem and partition by 32M to fit the
>>> LUKS header
>>> @@ -90,14 +93,25 @@ reencrypt_existing_partition() {
>>>       squashfs|swap|erofs|"")
>>>           [ "$debug" = "y" ] && echo "skip disk resize as it is not
>>> supported or unnecessary for fstype: '$partition_fstype'"
>>>           ;;
>>> +    luks)
>>> +        # Check if reencrypt was aborted
>>> +        if /usr/sbin/cryptsetup luksDump --batch-mode "$1" \
>>> +                | grep -q "online-reencrypt-v2"; then
>>> +            /usr/sbin/cryptsetup repair --batch-mode "$1" < "$2" || \
>>> +                panic "cryptsetup repair was not successful"
>>> +        fi
>>> +
>>> +        # already luks partition, don't resize
>>> +        CRYPTSETUP_PARAMS=""
>>> +        ;;
>>>       *)
>>>           panic "cannot resize partition, unsupported fstype:
>>> '$partition_fstype'"
>>>           ;;
>>>       esac
>>>       if [ -x /usr/sbin/cryptsetup-reencrypt ]; then
>>> -        /usr/sbin/cryptsetup-reencrypt --new --reduce-device-size
>>> "$reduce_device_size"k "$1" < "$2"
>>> +        /usr/sbin/cryptsetup-reencrypt --new ${CRYPTSETUP_PARAMS}
>>> "$1" < "$2"
>>>       else
>>> -        /usr/sbin/cryptsetup reencrypt --encrypt --reduce-device-
>>> size "$reduce_device_size"k "$1" < "$2"
>>> +        /usr/sbin/cryptsetup reencrypt --encrypt
>>> ${CRYPTSETUP_PARAMS} "$1" < "$2"
>>>       fi
>>>   }
>>> @@ -219,7 +233,7 @@ for partition_set in $partition_sets; do
>>>       # If partition is already encrypted, decrypt and continue with
>>> next partition:
>>>       decrypted_part=/dev/mapper/"$crypt_mount_name"
>>>       if /usr/sbin/cryptsetup luksDump --batch-mode "$part_device" \
>>> -            | grep -q "luks2"; then
>>> +            | grep -q "systemd-tpm2"; then
>> This should break encryption with clevis.
>
> I don't have a test setup for clevis, so any input on how to fix this,
> would be great.

If you set `CRYPT_BACKEND = "clevis" ` in the kas configuration of
isar-cip-core. You directly use clevis instead of systemd.

https://gitlab.com/cip-project/cip-core/isar-cip-core/-/blob/master/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.6.bb?ref_type=heads#L25

Quirin
>
>>
>>>           open_tpm2_partition "$part_device" "$crypt_mount_name"
>>> "$tpm_device"
>>>           # check if we are trying to mount root, set ROOT to
>>> decrypted partition:
>>> @@ -230,6 +244,12 @@ for partition_set in $partition_sets; do
>>>           continue
>>>       fi
>>> +    # If partition contains an aborted reencrypt luks header, switch
>>> to reencrypt mode:
>>> +    if /usr/sbin/cryptsetup luksDump --batch-mode "${part_device}" \
>>> +            | grep -q "online-reencrypt-v2"; then
>>> +        partition_format="reencrypt"
>>> +    fi
>>> +
>>>       # If partition should not be encrypted, continue with next
>>> partition:
>>>       if [ "$partition_format" = "noencrypt" ]
>>>       then
>>> @@ -247,10 +267,11 @@ for partition_set in $partition_sets; do
>>>           watchdog_pid=$!
>>>       fi
>>> -    # create random password for initial encryption
>>> -    # this will be dropped after reboot
>>> +    # use partuuid of the partition for initial encryption password,
>>> this key
>>> +    # will be removed after the reencryption has finished and the
>>> TPM2 token is
>>> +    # registered:
>>>       tmp_key=/tmp/"$(basename "$part_device")-lukskey"
>>> -    openssl rand -base64 32 > "$tmp_key"
>>> +    lsblk -no partuuid "$part_device" > "$tmp_key"
>>
>> This has the security implication that until the tpm key is used the
>> device is insecure.
>
> Yes. As I noted in the commit message.
>
> The other method, I thought about was using the tpm2, where I am
> encrypting a known key, like the partuuid with an internal tpm2 key, and
> using the resulting encrypted blob as a hexstring for the password.
>
> But I am not very familiar with TPM2 on how to properly do that.
>
> Other options would be to create a password and store it in the TPM2
> NVRAM, but that would not be very secure as well.
>
> So any suggestions for improvment here would be great.
>
> regards,
> Claudius
>
>>
>> Quirin
>>>       case "${partition_format}" in
>>>           "reencrypt")
>>> diff --git a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-
>>> hook_0.6.bb b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-
>>> hook_0.6.bb
>>> index c9a7f89..5e82521 100644
>>> --- a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.6.bb
>>> +++ b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.6.bb
>>> @@ -14,7 +14,6 @@ require recipes-initramfs/initramfs-hook/hook.inc
>>>   DEBIAN_DEPENDS .= ", \
>>>       cryptsetup, \
>>>       awk, \
>>> -    openssl, \
>>>       e2fsprogs, \
>>>       tpm2-tools, \
>>>       coreutils, \
>>> @@ -40,8 +39,8 @@ HOOK_ADD_MODULES = " \
>>>       ecb aes_generic xts"
>>>   HOOK_COPY_EXECS = " \
>>> -    openssl mke2fs grep awk expr seq sleep basename uuidparse
>>> mountpoint \
>>> -    e2fsck resize2fs cryptsetup cmp \
>>> +    mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
>>> +    e2fsck resize2fs cryptsetup cmp lsblk \
>>>       tpm2_pcrread tpm2_testparms tpm2_flushcontext \
>>>       /usr/lib/*/libgcc_s.so.1"
>>
>