* [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2
2025-06-16 9:30 [PATCH v7 0/4] initramfs-crypt-hook patch Claudius Heine
@ 2025-06-16 9:30 ` Claudius Heine
2025-06-16 10:06 ` Jan Kiszka
2025-06-16 9:30 ` [PATCH v7 2/4] initramfs-crypt-hook: add re-encryption recovery Claudius Heine
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Claudius Heine @ 2025-06-16 9:30 UTC (permalink / raw)
To: cip-dev
Cc: Jan Kiszka, Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan,
Claudius Heine
cryptsetup and systemd-cryptenroll do not currently support encrypting
partitions directly with keys stored in the TPM. For that reason a
temporary random password is used for the initial encryption, which will
get removed once the TPM2 token is added to the partition.
However this process does not allow continuing with the encryption
process in case of power failure.
For that reason store the initial encryption password in the TPM,
protected via measured boot based on the TPM2-PCR:7 register, which is
the default for `systemd-cryptenroll` and load it if it exists.
Signed-off-by: Claudius Heine <ch@denx.de>
---
.../initramfs-crypt-hook/files/local-top-complete | 56 ++++++++++++++++++++--
.../initramfs-crypt-hook_0.7.bb | 13 ++---
2 files changed, 60 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 ae0dcef4cb62a135beb6d4229237144b6d4edf8b..e511e8ef2d0bd4f4c8fd2fca248312dee173d224 100644
--- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
+++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
@@ -216,6 +216,54 @@ if [ ! -e "$tpm_device" ]; then
panic "No tpm device exists or supports pcr_hash '$pcr_bank_hash_type' or '$tpm_key_algorithm' - cannot create a encrypted device!"
fi
+export TPM2TOOLS_TCTI="device:${tpm_device}"
+TPM2_HANDLE="0x81080001"
+tpm2_key_get() {
+ key_file="$1"
+ TPM2_WORKDIR="/tmp/tpm2"
+
+ if ! tpm2_getcap handles-persistent | grep -q "$TPM2_HANDLE"; then
+ mkdir -p "${TPM2_WORKDIR}"
+ # Open session:
+ tpm2_startauthsession -S "${TPM2_WORKDIR}/session.ctx"
+ tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" \
+ -l "${HASH_TYPE}:7" \
+ -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
+ tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
+
+ # Add new key to tpm:
+ tpm2_createprimary -Q -C o -c "${TPM2_WORKDIR}/prim.ctx"
+ echo "$(tr -dc 'A-Za-z0-9_' </dev/urandom | head -c 32)" | \
+ tpm2_create -Q -g "sha256" \
+ -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
+ -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
+ -i- \
+ -C "${TPM2_WORKDIR}/prim.ctx" \
+ -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
+ tpm2_load -Q \
+ -C "${TPM2_WORKDIR}/prim.ctx" \
+ -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
+ -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
+ -n "${TPM2_WORKDIR}/pcr_seal_key.name" \
+ -c "${TPM2_WORKDIR}/pcr_seal_key.ctx"
+ tpm2_evictcontrol -c "${TPM2_WORKDIR}/pcr_seal_key.ctx" \
+ "$TPM2_HANDLE" -C o
+
+ rm -rf "${TPM2_WORKDIR}"
+ fi
+
+ mkdir -p "${TPM2_WORKDIR}"
+ tpm2_startauthsession --policy-session -S "${TPM2_WORKDIR}/session.ctx"
+ tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" -l "${HASH_TYPE}:7"
+ tpm2_unseal -p "session:${TPM2_WORKDIR}/session.ctx" -c "$TPM2_HANDLE" >"$key_file"
+ tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
+ rm -rf "${TPM2_WORKDIR}"
+}
+
+tpm2_key_del() {
+ tpm2_evictcontrol -C o -c "$TPM2_HANDLE"
+}
+
prepare_for_encryption
for partition_set in $partition_sets; do
@@ -259,16 +307,17 @@ for partition_set in $partition_sets; do
watchdog_pid=$!
fi
- # create random password for initial encryption
- # this will be dropped after reboot
+ # create random password, store it in the tpm2 encrypted and protected with
+ # PCR:7 registers to allow continuing reencryption in case of power-failure.
tmp_key=/tmp/"$(basename "$part_device")-lukskey"
- openssl rand -base64 32 > "$tmp_key"
+ tpm2_key_get "${tmp_key}"
case "${partition_format}" in
"reencrypt")
log_begin_msg "Encryption of ${part_device}"
reencrypt_existing_partition "$part_device" "$tmp_key"
enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
+ tpm2_key_del
open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
log_end_msg
;;
@@ -277,6 +326,7 @@ for partition_set in $partition_sets; do
/usr/sbin/cryptsetup luksFormat --batch-mode \
--type luks2 "$part_device" < "$tmp_key"
enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
+ tpm2_key_del
open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
eval "${create_file_system_cmd} ${decrypted_part}"
log_end_msg
diff --git a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
index 80a4755e4e0acfc96bb2d0f8e175e2787473e384..b48c6e89384f7f43737fdbb3438d5be15bb5945e 100644
--- a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
+++ b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
@@ -14,7 +14,6 @@ require recipes-initramfs/initramfs-hook/hook.inc
DEBIAN_DEPENDS .= ", \
cryptsetup, \
awk, \
- openssl, \
e2fsprogs, \
tpm2-tools, \
coreutils, \
@@ -40,19 +39,21 @@ HOOK_ADD_MODULES = " \
ecb aes_generic xts"
HOOK_COPY_EXECS = " \
- openssl mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
- e2fsck resize2fs cryptsetup \
- tpm2_pcrread tpm2_testparms tpm2_flushcontext \
+ mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
+ e2fsck resize2fs cryptsetup head tr rm \
+ tpm2_pcrread tpm2_testparms tpm2_flushcontext tpm2_startauthsession \
+ tpm2_policypcr tpm2_createprimary tpm2_create tpm2_load tpm2_evictcontrol \
+ tpm2_unseal tpm2_getcap \
/usr/lib/*/libgcc_s.so.1"
HOOK_COPY_EXECS:append:clevis = " \
clevis clevis-decrypt clevis-encrypt-tpm2 clevis-decrypt-tpm2 \
clevis-luks-bind clevis-luks-unlock \
clevis-luks-list clevis-luks-common-functions \
- tpm2_createprimary tpm2_unseal tpm2_create tpm2_load tpm2_createpolicy \
+ tpm2_createpolicy \
bash luksmeta jose sed tail sort rm mktemp pwmake file"
HOOK_COPY_EXECS:append:systemd = " \
- systemd-cryptenroll tpm2_pcrread tpm2_testparms \
+ systemd-cryptenroll \
/usr/lib/systemd/systemd-cryptsetup \
/usr/lib/*/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2
2025-06-16 9:30 ` [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2 Claudius Heine
@ 2025-06-16 10:06 ` Jan Kiszka
2025-06-26 9:09 ` Claudius Heine
0 siblings, 1 reply; 7+ messages in thread
From: Jan Kiszka @ 2025-06-16 10:06 UTC (permalink / raw)
To: Claudius Heine, cip-dev
Cc: Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan
On 16.06.25 11:30, Claudius Heine wrote:
> cryptsetup and systemd-cryptenroll do not currently support encrypting
> partitions directly with keys stored in the TPM. For that reason a
> temporary random password is used for the initial encryption, which will
> get removed once the TPM2 token is added to the partition.
BTW, did you already try to discuss this use case with systemd at least?
Not to see as pushback for these changes, but maybe we can once simplify
things again if upstream address this as well.
Jan
>
> However this process does not allow continuing with the encryption
> process in case of power failure.
>
> For that reason store the initial encryption password in the TPM,
> protected via measured boot based on the TPM2-PCR:7 register, which is
> the default for `systemd-cryptenroll` and load it if it exists.
>
> Signed-off-by: Claudius Heine <ch@denx.de>
> ---
> .../initramfs-crypt-hook/files/local-top-complete | 56 ++++++++++++++++++++--
> .../initramfs-crypt-hook_0.7.bb | 13 ++---
> 2 files changed, 60 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 ae0dcef4cb62a135beb6d4229237144b6d4edf8b..e511e8ef2d0bd4f4c8fd2fca248312dee173d224 100644
> --- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
> +++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
> @@ -216,6 +216,54 @@ if [ ! -e "$tpm_device" ]; then
> panic "No tpm device exists or supports pcr_hash '$pcr_bank_hash_type' or '$tpm_key_algorithm' - cannot create a encrypted device!"
> fi
>
> +export TPM2TOOLS_TCTI="device:${tpm_device}"
> +TPM2_HANDLE="0x81080001"
> +tpm2_key_get() {
> + key_file="$1"
> + TPM2_WORKDIR="/tmp/tpm2"
> +
> + if ! tpm2_getcap handles-persistent | grep -q "$TPM2_HANDLE"; then
> + mkdir -p "${TPM2_WORKDIR}"
> + # Open session:
> + tpm2_startauthsession -S "${TPM2_WORKDIR}/session.ctx"
> + tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" \
> + -l "${HASH_TYPE}:7" \
> + -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
> + tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
> +
> + # Add new key to tpm:
> + tpm2_createprimary -Q -C o -c "${TPM2_WORKDIR}/prim.ctx"
> + echo "$(tr -dc 'A-Za-z0-9_' </dev/urandom | head -c 32)" | \
> + tpm2_create -Q -g "sha256" \
> + -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
> + -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
> + -i- \
> + -C "${TPM2_WORKDIR}/prim.ctx" \
> + -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
> + tpm2_load -Q \
> + -C "${TPM2_WORKDIR}/prim.ctx" \
> + -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
> + -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
> + -n "${TPM2_WORKDIR}/pcr_seal_key.name" \
> + -c "${TPM2_WORKDIR}/pcr_seal_key.ctx"
> + tpm2_evictcontrol -c "${TPM2_WORKDIR}/pcr_seal_key.ctx" \
> + "$TPM2_HANDLE" -C o
> +
> + rm -rf "${TPM2_WORKDIR}"
> + fi
> +
> + mkdir -p "${TPM2_WORKDIR}"
> + tpm2_startauthsession --policy-session -S "${TPM2_WORKDIR}/session.ctx"
> + tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" -l "${HASH_TYPE}:7"
> + tpm2_unseal -p "session:${TPM2_WORKDIR}/session.ctx" -c "$TPM2_HANDLE" >"$key_file"
> + tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
> + rm -rf "${TPM2_WORKDIR}"
> +}
> +
> +tpm2_key_del() {
> + tpm2_evictcontrol -C o -c "$TPM2_HANDLE"
> +}
> +
> prepare_for_encryption
>
> for partition_set in $partition_sets; do
> @@ -259,16 +307,17 @@ for partition_set in $partition_sets; do
> watchdog_pid=$!
> fi
>
> - # create random password for initial encryption
> - # this will be dropped after reboot
> + # create random password, store it in the tpm2 encrypted and protected with
> + # PCR:7 registers to allow continuing reencryption in case of power-failure.
> tmp_key=/tmp/"$(basename "$part_device")-lukskey"
> - openssl rand -base64 32 > "$tmp_key"
> + tpm2_key_get "${tmp_key}"
>
> case "${partition_format}" in
> "reencrypt")
> log_begin_msg "Encryption of ${part_device}"
> reencrypt_existing_partition "$part_device" "$tmp_key"
> enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
> + tpm2_key_del
> open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
> log_end_msg
> ;;
> @@ -277,6 +326,7 @@ for partition_set in $partition_sets; do
> /usr/sbin/cryptsetup luksFormat --batch-mode \
> --type luks2 "$part_device" < "$tmp_key"
> enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
> + tpm2_key_del
> open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
> eval "${create_file_system_cmd} ${decrypted_part}"
> log_end_msg
> diff --git a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
> index 80a4755e4e0acfc96bb2d0f8e175e2787473e384..b48c6e89384f7f43737fdbb3438d5be15bb5945e 100644
> --- a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
> +++ b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
> @@ -14,7 +14,6 @@ require recipes-initramfs/initramfs-hook/hook.inc
> DEBIAN_DEPENDS .= ", \
> cryptsetup, \
> awk, \
> - openssl, \
> e2fsprogs, \
> tpm2-tools, \
> coreutils, \
> @@ -40,19 +39,21 @@ HOOK_ADD_MODULES = " \
> ecb aes_generic xts"
>
> HOOK_COPY_EXECS = " \
> - openssl mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
> - e2fsck resize2fs cryptsetup \
> - tpm2_pcrread tpm2_testparms tpm2_flushcontext \
> + mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
> + e2fsck resize2fs cryptsetup head tr rm \
> + tpm2_pcrread tpm2_testparms tpm2_flushcontext tpm2_startauthsession \
> + tpm2_policypcr tpm2_createprimary tpm2_create tpm2_load tpm2_evictcontrol \
> + tpm2_unseal tpm2_getcap \
> /usr/lib/*/libgcc_s.so.1"
>
> HOOK_COPY_EXECS:append:clevis = " \
> clevis clevis-decrypt clevis-encrypt-tpm2 clevis-decrypt-tpm2 \
> clevis-luks-bind clevis-luks-unlock \
> clevis-luks-list clevis-luks-common-functions \
> - tpm2_createprimary tpm2_unseal tpm2_create tpm2_load tpm2_createpolicy \
> + tpm2_createpolicy \
> bash luksmeta jose sed tail sort rm mktemp pwmake file"
> HOOK_COPY_EXECS:append:systemd = " \
> - systemd-cryptenroll tpm2_pcrread tpm2_testparms \
> + systemd-cryptenroll \
> /usr/lib/systemd/systemd-cryptsetup \
> /usr/lib/*/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
>
>
--
Siemens AG, Foundational Technologies
Linux Expert Center
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2
2025-06-16 10:06 ` Jan Kiszka
@ 2025-06-26 9:09 ` Claudius Heine
0 siblings, 0 replies; 7+ messages in thread
From: Claudius Heine @ 2025-06-26 9:09 UTC (permalink / raw)
To: Jan Kiszka, cip-dev; +Cc: Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan
On Mon Jun 16, 2025 at 12:06 PM CEST, Jan Kiszka wrote:
> On 16.06.25 11:30, Claudius Heine wrote:
>> cryptsetup and systemd-cryptenroll do not currently support encrypting
>> partitions directly with keys stored in the TPM. For that reason a
>> temporary random password is used for the initial encryption, which will
>> get removed once the TPM2 token is added to the partition.
>
> BTW, did you already try to discuss this use case with systemd at least?
> Not to see as pushback for these changes, but maybe we can once simplify
> things again if upstream address this as well.
I talked with systemd-devel about this:
https://lists.freedesktop.org/archives/systemd-devel/2025-June/051456.html
And from what I gather is that reencryption isn't supported by systemd
and likely will stay that way.
I was pointed to systemd-repart, but that doesn't have reencryption
support:
https://github.com/systemd/systemd/pull/29731
From what I gather, they have a very different approach to encrypting
file systems, that isn't compatible to the custom scripting that was
done here in cip-core.
IIUC: They already assume that some key was programmed into the TPM2
(SRK-key), and then use systemd-repart to create new encrypted
partitions using that key and use `CopyFiles` directive to copy plain
text files to the encrypted file system from somewhere.
regards,
Claudius
>
> Jan
>
>>
>> However this process does not allow continuing with the encryption
>> process in case of power failure.
>>
>> For that reason store the initial encryption password in the TPM,
>> protected via measured boot based on the TPM2-PCR:7 register, which is
>> the default for `systemd-cryptenroll` and load it if it exists.
>>
>> Signed-off-by: Claudius Heine <ch@denx.de>
>> ---
>> .../initramfs-crypt-hook/files/local-top-complete | 56 ++++++++++++++++++++--
>> .../initramfs-crypt-hook_0.7.bb | 13 ++---
>> 2 files changed, 60 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 ae0dcef4cb62a135beb6d4229237144b6d4edf8b..e511e8ef2d0bd4f4c8fd2fca248312dee173d224 100644
>> --- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
>> +++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
>> @@ -216,6 +216,54 @@ if [ ! -e "$tpm_device" ]; then
>> panic "No tpm device exists or supports pcr_hash '$pcr_bank_hash_type' or '$tpm_key_algorithm' - cannot create a encrypted device!"
>> fi
>>
>> +export TPM2TOOLS_TCTI="device:${tpm_device}"
>> +TPM2_HANDLE="0x81080001"
>> +tpm2_key_get() {
>> + key_file="$1"
>> + TPM2_WORKDIR="/tmp/tpm2"
>> +
>> + if ! tpm2_getcap handles-persistent | grep -q "$TPM2_HANDLE"; then
>> + mkdir -p "${TPM2_WORKDIR}"
>> + # Open session:
>> + tpm2_startauthsession -S "${TPM2_WORKDIR}/session.ctx"
>> + tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" \
>> + -l "${HASH_TYPE}:7" \
>> + -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
>> + tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
>> +
>> + # Add new key to tpm:
>> + tpm2_createprimary -Q -C o -c "${TPM2_WORKDIR}/prim.ctx"
>> + echo "$(tr -dc 'A-Za-z0-9_' </dev/urandom | head -c 32)" | \
>> + tpm2_create -Q -g "sha256" \
>> + -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
>> + -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
>> + -i- \
>> + -C "${TPM2_WORKDIR}/prim.ctx" \
>> + -L "${TPM2_WORKDIR}/pcr7.${HASH_TYPE}.policy"
>> + tpm2_load -Q \
>> + -C "${TPM2_WORKDIR}/prim.ctx" \
>> + -u "${TPM2_WORKDIR}/pcr_seal_key.pub" \
>> + -r "${TPM2_WORKDIR}/pcr_seal_key.priv" \
>> + -n "${TPM2_WORKDIR}/pcr_seal_key.name" \
>> + -c "${TPM2_WORKDIR}/pcr_seal_key.ctx"
>> + tpm2_evictcontrol -c "${TPM2_WORKDIR}/pcr_seal_key.ctx" \
>> + "$TPM2_HANDLE" -C o
>> +
>> + rm -rf "${TPM2_WORKDIR}"
>> + fi
>> +
>> + mkdir -p "${TPM2_WORKDIR}"
>> + tpm2_startauthsession --policy-session -S "${TPM2_WORKDIR}/session.ctx"
>> + tpm2_policypcr -Q -S "${TPM2_WORKDIR}/session.ctx" -l "${HASH_TYPE}:7"
>> + tpm2_unseal -p "session:${TPM2_WORKDIR}/session.ctx" -c "$TPM2_HANDLE" >"$key_file"
>> + tpm2_flushcontext "${TPM2_WORKDIR}/session.ctx"
>> + rm -rf "${TPM2_WORKDIR}"
>> +}
>> +
>> +tpm2_key_del() {
>> + tpm2_evictcontrol -C o -c "$TPM2_HANDLE"
>> +}
>> +
>> prepare_for_encryption
>>
>> for partition_set in $partition_sets; do
>> @@ -259,16 +307,17 @@ for partition_set in $partition_sets; do
>> watchdog_pid=$!
>> fi
>>
>> - # create random password for initial encryption
>> - # this will be dropped after reboot
>> + # create random password, store it in the tpm2 encrypted and protected with
>> + # PCR:7 registers to allow continuing reencryption in case of power-failure.
>> tmp_key=/tmp/"$(basename "$part_device")-lukskey"
>> - openssl rand -base64 32 > "$tmp_key"
>> + tpm2_key_get "${tmp_key}"
>>
>> case "${partition_format}" in
>> "reencrypt")
>> log_begin_msg "Encryption of ${part_device}"
>> reencrypt_existing_partition "$part_device" "$tmp_key"
>> enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
>> + tpm2_key_del
>> open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
>> log_end_msg
>> ;;
>> @@ -277,6 +326,7 @@ for partition_set in $partition_sets; do
>> /usr/sbin/cryptsetup luksFormat --batch-mode \
>> --type luks2 "$part_device" < "$tmp_key"
>> enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
>> + tpm2_key_del
>> open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
>> eval "${create_file_system_cmd} ${decrypted_part}"
>> log_end_msg
>> diff --git a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
>> index 80a4755e4e0acfc96bb2d0f8e175e2787473e384..b48c6e89384f7f43737fdbb3438d5be15bb5945e 100644
>> --- a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
>> +++ b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
>> @@ -14,7 +14,6 @@ require recipes-initramfs/initramfs-hook/hook.inc
>> DEBIAN_DEPENDS .= ", \
>> cryptsetup, \
>> awk, \
>> - openssl, \
>> e2fsprogs, \
>> tpm2-tools, \
>> coreutils, \
>> @@ -40,19 +39,21 @@ HOOK_ADD_MODULES = " \
>> ecb aes_generic xts"
>>
>> HOOK_COPY_EXECS = " \
>> - openssl mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
>> - e2fsck resize2fs cryptsetup \
>> - tpm2_pcrread tpm2_testparms tpm2_flushcontext \
>> + mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
>> + e2fsck resize2fs cryptsetup head tr rm \
>> + tpm2_pcrread tpm2_testparms tpm2_flushcontext tpm2_startauthsession \
>> + tpm2_policypcr tpm2_createprimary tpm2_create tpm2_load tpm2_evictcontrol \
>> + tpm2_unseal tpm2_getcap \
>> /usr/lib/*/libgcc_s.so.1"
>>
>> HOOK_COPY_EXECS:append:clevis = " \
>> clevis clevis-decrypt clevis-encrypt-tpm2 clevis-decrypt-tpm2 \
>> clevis-luks-bind clevis-luks-unlock \
>> clevis-luks-list clevis-luks-common-functions \
>> - tpm2_createprimary tpm2_unseal tpm2_create tpm2_load tpm2_createpolicy \
>> + tpm2_createpolicy \
>> bash luksmeta jose sed tail sort rm mktemp pwmake file"
>> HOOK_COPY_EXECS:append:systemd = " \
>> - systemd-cryptenroll tpm2_pcrread tpm2_testparms \
>> + systemd-cryptenroll \
>> /usr/lib/systemd/systemd-cryptsetup \
>> /usr/lib/*/cryptsetup/libcryptsetup-token-systemd-tpm2.so"
>>
>>
--
DENX Software Engineering GmbH, Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-54 Fax: (+49)-8142-66989-80 Email: ch@denx.de
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v7 2/4] initramfs-crypt-hook: add re-encryption recovery
2025-06-16 9:30 [PATCH v7 0/4] initramfs-crypt-hook patch Claudius Heine
2025-06-16 9:30 ` [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2 Claudius Heine
@ 2025-06-16 9:30 ` Claudius Heine
2025-06-16 9:30 ` [PATCH v7 3/4] initramfs-crypt-hook: implement 'noencrypt' option Claudius Heine
2025-06-16 9:30 ` [PATCH v7 4/4] initramfs-crypt-hook: add 'format-if-empty' feature Claudius Heine
3 siblings, 0 replies; 7+ messages in thread
From: Claudius Heine @ 2025-06-16 9:30 UTC (permalink / raw)
To: cip-dev
Cc: Jan Kiszka, Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan,
Claudius Heine
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/clevis token can be installed
- The LUKS header does not contain 'systemd-tpm2'/'clevis', thus it
needs to be registered and the temporary encryption key needs to be
removed
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>
---
.../initramfs-crypt-hook/files/local-top-complete | 26 +++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
index e511e8ef2d0bd4f4c8fd2fca248312dee173d224..6df4cccee9c99653fa9869ca2e46ead549d6fc2d 100644
--- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
+++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
@@ -73,6 +73,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
@@ -91,14 +94,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
}
@@ -296,11 +310,17 @@ for partition_set in $partition_sets; do
fi
if /usr/sbin/cryptsetup luksDump --batch-mode "$part_device" \
- | grep -q "luks2"; then
+ | grep -q "systemd-tpm2\|clevis"; then
open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
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
+
# service watchdog in the background during lengthy re-encryption
if [ -z "$watchdog_pid" ]; then
service_watchdog &
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v7 3/4] initramfs-crypt-hook: implement 'noencrypt' option
2025-06-16 9:30 [PATCH v7 0/4] initramfs-crypt-hook patch Claudius Heine
2025-06-16 9:30 ` [PATCH v7 1/4] initramfs-crypt-hook: store initial encryption key in TPM2 Claudius Heine
2025-06-16 9:30 ` [PATCH v7 2/4] initramfs-crypt-hook: add re-encryption recovery Claudius Heine
@ 2025-06-16 9:30 ` Claudius Heine
2025-06-16 9:30 ` [PATCH v7 4/4] initramfs-crypt-hook: add 'format-if-empty' feature Claudius Heine
3 siblings, 0 replies; 7+ messages in thread
From: Claudius Heine @ 2025-06-16 9:30 UTC (permalink / raw)
To: cip-dev
Cc: Jan Kiszka, Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan,
Claudius Heine
In case encryption needs to be enabled via an update, while still
allowing the update fall back to work. One update step where encryption
is supported, but no reencryption is taking place if the device is not
encrypted.
For this the `noencrypt` hook is implemented, which requires some
restructure/reordering of the `local-top-complete` script.
Signed-off-by: Claudius Heine <ch@denx.de>
---
doc/README.tpm2.encryption.md | 22 +++++++++++++++++++-
.../initramfs-crypt-hook/files/local-top-complete | 24 +++++++++++++++++-----
2 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/doc/README.tpm2.encryption.md b/doc/README.tpm2.encryption.md
index 3f7e89f34fa4638a42285989e61370d8970afcde..2256f95a039044313807ab75ce219fa3eb7408b6 100644
--- a/doc/README.tpm2.encryption.md
+++ b/doc/README.tpm2.encryption.md
@@ -42,11 +42,12 @@ The initramfs-crypt-hook recipe has the following variables which can be overwri
### CRYPT_PARTITIONS
The variable `CRYPT_PARTITIONS` contains the information which partition shall be encrypted where to mount it.
-Each entry uses the schema `<partition-identifier>:<mountpoint>:<reencrypt or format>`.
+Each entry uses the schema `<partition-identifier>:<mountpoint>:<reencrypt | format | noencrypt>`.
- The `partition-idenitifer` is used to identify the partition on the disk, it can contain a partition label, partition UUID or absolute path to the partition device, e.g. `/dev/sda`.
- The `mountpoint` is used mount the decrypted partition in the root file system
- `reencrypt` uses `cryptsetup reencrypt` to encrypt the exiting content of the partition. This reduces the partition by 32MB and the file system by a similar amount
- `format` creates a empty LUKS partition and creates a file system defined with the shell command given in `CRYPT_CREATE_FILE_SYSTEM_CMD`
+- `noencrypt` will not try to encrypt the partition if it isn't encrypted already, but will open it if it is. See the section [Encrypting the shared partition via an update](#### Encrypting the shared partition via an update) for more information
#### Encrypted root file system
@@ -58,6 +59,25 @@ The mountpoint is empty as the root partition is mounted by a seperate initramf
Both partitions are encrypted during first boot. The initramfs hook opens `${ABROOTFS_PART_UUID_A}` and `${ABROOTFS_PART_UUID_B}`
during boot.
+#### Encrypting the shared partition via an update
+
+With the following requirements, special handling is necessary:
+
+- A/B update scheme is used.
+- Both slots have a shared volume that needs to be encrypted as well.
+- The system in the field is currently unencrypted, and encryption should be added via an update.
+- When the update fails, the fallback system needs to deal with an encrypted data partition.
+
+In this case, the fallback system needs to support an encrypted shared data partition but would not encrypt it on its own. For this, the `noencrypt` flag can be used.
+
+The data partition in the fallback system will have the `noencrypt` flag set, while the update system will set the flag to `reencrypt`. This will handle the following case:
+
+- Unencrypted system on slot A is running; the shared data partition has set the `noencrypt` flag and is not encrypted.
+- Update for enabling encryption is applied to slot B, where the shared data partition has the `reencrypt` flag.
+- System reboots to slot B, encrypting the shared data partition.
+- Update fails at a later point and is not blessed; system reboots into the fallback system on slot A.
+- Fallback system now needs to be able to use the shared data partition.
+
### CRYPT_CREATE_FILE_SYSTEM_CMD
The variable `CRYPT_CREATE_FILE_SYSTEM_CMD` contains the command to create a new file system on a newly
diff --git a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
index 6df4cccee9c99653fa9869ca2e46ead549d6fc2d..19df9ac9e22fde09dda8ea3ad76f7ff763d3cf1b 100644
--- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
+++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
@@ -303,15 +303,18 @@ for partition_set in $partition_sets; do
if [ ! -e "$part_device" ]; then
panic "Could not find device mapped to '$partition' cannot be encrypted!"
fi
- decrypted_part=/dev/mapper/"$crypt_mount_name"
- # check if we are trying to mount root
- if [ "$partition_mountpoint" = "/" ]; then
- echo "ROOT=$decrypted_part" >/conf/param.conf
- fi
+ # 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 "systemd-tpm2\|clevis"; then
open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
+
+ # check if we are trying to mount root, set ROOT to decrypted partition:
+ if [ "$partition_mountpoint" = "/" ]; then
+ echo "ROOT=$decrypted_part" >/conf/param.conf
+ fi
+
continue
fi
@@ -321,6 +324,17 @@ for partition_set in $partition_sets; do
partition_format="reencrypt"
fi
+ # If partition should not be encrypted, continue with next partition:
+ if [ "$partition_format" = "noencrypt" ]
+ then
+ continue
+ fi
+
+ # check if we are trying to mount root, set ROOT to decrypted partition:
+ if [ "$partition_mountpoint" = "/" ]; then
+ echo "ROOT=$decrypted_part" >/conf/param.conf
+ fi
+
# service watchdog in the background during lengthy re-encryption
if [ -z "$watchdog_pid" ]; then
service_watchdog &
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v7 4/4] initramfs-crypt-hook: add 'format-if-empty' feature
2025-06-16 9:30 [PATCH v7 0/4] initramfs-crypt-hook patch Claudius Heine
` (2 preceding siblings ...)
2025-06-16 9:30 ` [PATCH v7 3/4] initramfs-crypt-hook: implement 'noencrypt' option Claudius Heine
@ 2025-06-16 9:30 ` Claudius Heine
3 siblings, 0 replies; 7+ messages in thread
From: Claudius Heine @ 2025-06-16 9:30 UTC (permalink / raw)
To: cip-dev
Cc: Jan Kiszka, Quirin Gylstorff, Heinisch Alexander, Cetin Gokhan,
Claudius Heine
With the A/B update scheme, the goal is to have two (mostly) independent
system slots, one that is active and one that is inactive. The active
system slot writes updates to the inactive system slot and switches the
active and inactive slot via a reboot. Late when booting the new system,
it will decide if the current system is stable and then 'bless' it in
the bootloader, or it will consider the update failed and the system
reboots, without the updated system slot being blessed.
In the last case, the system in the old slot will work as a fallback
system, that reports the failed update back to the backend, and continue
the service, as it has before the update was applied.
It is important here that the update does not modify the fallback system
in any avoidable way, because doing so might break the fallback and make
the system not remotely recoverable.
If encryption is added via an update, there are two cases to consider:
1. Update is applied to the inactive update slot via the normal update
procedure. In this case the fallback system should not be modified.
2. A system that uses encryption is flashed via a factory flash. In this
case there is no fallback system and all partitions of both update
slots need to be encrypted.
To differentiate between these cases, the content of each fallback
partition can be looked at, if they contain data, we are in case (1) and
these partitions should be left alone, if they contain only 0x00, they
can be formatted, because we are in case (2).
The `format-if-empty` option is implemented here, will look if the first
10 MiB of each partition marked with this option contains 0x00 or not
and will either format it or leave it alone.
Signed-off-by: Claudius Heine <ch@denx.de>
---
doc/README.tpm2.encryption.md | 5 ++++-
.../initramfs-crypt-hook/files/local-top-complete | 21 +++++++++++++++++++++
.../initramfs-crypt-hook_0.7.bb | 2 +-
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/doc/README.tpm2.encryption.md b/doc/README.tpm2.encryption.md
index 2256f95a039044313807ab75ce219fa3eb7408b6..3c29381209c5bec8aa0b031d941d23b7bf0cfd95 100644
--- a/doc/README.tpm2.encryption.md
+++ b/doc/README.tpm2.encryption.md
@@ -42,12 +42,13 @@ The initramfs-crypt-hook recipe has the following variables which can be overwri
### CRYPT_PARTITIONS
The variable `CRYPT_PARTITIONS` contains the information which partition shall be encrypted where to mount it.
-Each entry uses the schema `<partition-identifier>:<mountpoint>:<reencrypt | format | noencrypt>`.
+Each entry uses the schema `<partition-identifier>:<mountpoint>:<reencrypt | format | noencrypt | format-if-empty>`.
- The `partition-idenitifer` is used to identify the partition on the disk, it can contain a partition label, partition UUID or absolute path to the partition device, e.g. `/dev/sda`.
- The `mountpoint` is used mount the decrypted partition in the root file system
- `reencrypt` uses `cryptsetup reencrypt` to encrypt the exiting content of the partition. This reduces the partition by 32MB and the file system by a similar amount
- `format` creates a empty LUKS partition and creates a file system defined with the shell command given in `CRYPT_CREATE_FILE_SYSTEM_CMD`
- `noencrypt` will not try to encrypt the partition if it isn't encrypted already, but will open it if it is. See the section [Encrypting the shared partition via an update](#### Encrypting the shared partition via an update) for more information
+- `format-if-empty` will create an empty LUKS partition and format it, like the `format` option, but only if the first 10 MiB are empty (contain only 0x00). This makes it possible to differentiate if a partition is empty and can be encrypted, because it was freshly flashed via a factory image, or if it might contain an unencrypted fallback system and should be left alone.
#### Encrypted root file system
@@ -78,6 +79,8 @@ The data partition in the fallback system will have the `noencrypt` flag set, wh
- Update fails at a later point and is not blessed; system reboots into the fallback system on slot A.
- Fallback system now needs to be able to use the shared data partition.
+In this case, where encryption is added via an update, the `format-if-empty` option is also useful. The system with encryption enabled has the `format-if-empty` option set for the partition(s) in the inactive update slot. This will cause both sets of partitions in both slots to be encrypted after the first boot on a fresh factory flashed system, but will not disturb existing data of any fallback system if booted after an update.
+
### CRYPT_CREATE_FILE_SYSTEM_CMD
The variable `CRYPT_CREATE_FILE_SYSTEM_CMD` contains the command to create a new file system on a newly
diff --git a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
index 19df9ac9e22fde09dda8ea3ad76f7ff763d3cf1b..58a6cbcbb5b0880cb93d08a4b64b8b0635927702 100644
--- a/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
+++ b/recipes-initramfs/initramfs-crypt-hook/files/local-top-complete
@@ -365,6 +365,27 @@ for partition_set in $partition_sets; do
eval "${create_file_system_cmd} ${decrypted_part}"
log_end_msg
;;
+ "format-if-empty")
+ # Check if first 10MiB contain only zeros, or if it contains a luks
+ # header, but no TPM2 token
+ if cmp -s -n "$(( 10 * 1024 * 1024 ))" "${part_device}" /dev/zero || \
+ ( [ "$(get_fstype "${part_device}")" = "luks" ] && \
+ ! ( /usr/sbin/cryptsetup luksDump --batch-mode "$part_device" |
+ grep -q "systemd-tpm2\|clevis" ) )
+ then
+ log_begin_msg "Encryption of ${part_device}"
+ /usr/sbin/cryptsetup luksFormat --batch-mode \
+ --type luks2 "$part_device" < "$tmp_key"
+ enroll_tpm2_token "$part_device" "$tmp_key" "$tpm_device" "$tpm_key_algorithm" "$pcr_bank_hash_type"
+ tpm2_key_del
+ open_tpm2_partition "$part_device" "$crypt_mount_name" "$tpm_device"
+ eval "${create_file_system_cmd} ${decrypted_part}"
+ log_end_msg
+ else
+ # If not empty, leave it alone.
+ continue
+ fi
+ ;;
*)
panic "Unknown value ${partition_format}. Cannot create a encrypted partition !"
;;
diff --git a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
index b48c6e89384f7f43737fdbb3438d5be15bb5945e..2e5c99624661d807a986da291ffeb0c6e8f95926 100644
--- a/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
+++ b/recipes-initramfs/initramfs-crypt-hook/initramfs-crypt-hook_0.7.bb
@@ -40,7 +40,7 @@ HOOK_ADD_MODULES = " \
HOOK_COPY_EXECS = " \
mke2fs grep awk expr seq sleep basename uuidparse mountpoint \
- e2fsck resize2fs cryptsetup head tr rm \
+ e2fsck resize2fs cryptsetup head tr rm cmp \
tpm2_pcrread tpm2_testparms tpm2_flushcontext tpm2_startauthsession \
tpm2_policypcr tpm2_createprimary tpm2_create tpm2_load tpm2_evictcontrol \
tpm2_unseal tpm2_getcap \
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread