Repository: toradex/meta-toradex-security
Pull request #89: Implementing user encryption hook for tdx-enc-handler
Author: login: MMG-DG
Created: 2025-01-30T08:43:21Z | Merged: 2025-03-22T11:41:50Z

**Environment**
This issue was noticed when using the following components:
- tdx-reference-minimal-image
- iMX8MP Verdin SoM

**Issue**
As described in Issue [#39], if it is required to perform multiple reboots during production (e.g. for pre-testing and validation) before the SEC_BOOT fuse is "blown", then the encrypted partition will be inaccessible after blowing the eFuse.

**Cause**
This issue is caused because the partition is encrypted on first boot.
However when using the CAAM to perform the encryption, a test key is used until the SEC_BOOT eFuse is blown, at which point a real key is used.
Due to this the partition is encrypted with the test/demo key and is then not decryptable after the SEC_BOOT eFuse is blown.

**Current Workaround**
At present the process would have to be a manual step to decrypt the partition and removing the key blob before rebooting to blow the SEC_BOOT eFuse.
If this is done then the next re-boot will encrypt the partition again, this time with the real key.
This process is made more complex if there is existing initial data within the partition to be encrypted as this data will need to be "backed-up" and "restored" when decrypting the partition.
In certain industries this would have to be done in RAM using a tempFS, as some data is not allowed to be stored at any time on unencrypted media due to how easy it can be to "recover" the temporarily written data.

**Proposed Fix**
This is to add a user hook (via an external script) to prevent the initial encryption process. This will allow the user to perform any checks like SEC_BOOT fuse state before letting the encryption process proceed.

**Changes**
- Changes the process so data preservation is only checked when the partition is successfully mounted
- Adds a metod that calls out to an external script if it exists to allow users to disable the encryption process
- Updated documentation with details of the new hook
- Updated data-partition documentation to add missing variable assignment for mountpoint (fails to boot correctly if this is not set)


Diff:
diff --git a/docs/README-encryption.md b/docs/README-encryption.md
index d268684..6c97b3f 100644
--- a/docs/README-encryption.md
+++ b/docs/README-encryption.md
@@ -92,6 +92,10 @@ Be aware that, if you have a device with a partition encrypted with the test key
 
 To workaround this issue, you can manually remove the key blob, which is by default located at `/var/local/private/.keys/tdx-enc-key.blob`. After removing the key blob and rebooting the device, another key will be generated and the partition will be formatted and encrypted with the new key. As a consequence, you will lose any content on the partition previously encrypted with the test key.
 
+Alternatively, you can provide a script located at `/usr/sbin/encryption_allowed`, which will be used to trigger an `Encryption disabled by the user!` error and exit after initially mounting the partition (this will only occur if the partition has not already been encrypted). This could be used to check the system state, for example reading the SEC_BOOT eFuse (in the case of NXP iMX SoCs) and only enabling encryption once the OTPMK is used, thus avoiding issues when using the initial test key.
+
+NOTE: This user script must be owned and only writable by `root` (example Linux permission of 0755).
+
 ## Notes on using a TPM
 
 Before enabling the TPM backend, you need to make sure there is a TPM device available in the hardware and configured in the operating system. This usually involves enabling the device driver in the Linux kernel and adding a node in the device tree. Be aware that providing access to a TPM device is out of scope in this layer.
diff --git a/recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh b/recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
index 132d1e6..de7a338 100755
--- a/recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
+++ b/recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
@@ -260,15 +260,31 @@ tdx_enc_key_gen_tpm() {
                               "load \$(cat ${TDX_ENC_KEY_FULLPATH})"
 }
 
-# backup original data in partition (if not encrypted)
-tdx_enc_backup_data() {
-    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
-        tdx_enc_log "Data preservation is not enabled"
-        return 0
+# initially mount the partition if possible
+tdx_enc_pre_mount() {
+    tdx_enc_log "Attempting partition pre-mount"
+
+    if source=$(findmnt -no SOURCE "${TDX_ENC_STORAGE_MOUNTPOINT}"); then
+        if [ "${source}" = "${TDX_ENC_STORAGE_LOCATION}" ]; then
+            tdx_enc_log "Partition is already mounted to the specified location."
+            return 0
+        fi
+
+        tdx_enc_exit_error "Mount location already points to a different storage location."
     fi
 
     mkdir -p "${TDX_ENC_STORAGE_MOUNTPOINT}"
     if ! mount ${TDX_ENC_STORAGE_LOCATION} "${TDX_ENC_STORAGE_MOUNTPOINT}"; then
+        tdx_enc_log "Unable to mount location, possibly already encrypted..."
+        return 1
+    fi
+    return 0
+}
+
+# backup original data in partition (if not encrypted)
+tdx_enc_backup_data() {
+    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
+        tdx_enc_log "Data preservation is not enabled"
         return 0
     fi
 
@@ -282,8 +298,25 @@ tdx_enc_backup_data() {
     if [ "$?" -ne 0 ] || echo "${msgs}" | grep -qi 'error\|invalid'; then
         tdx_enc_exit_error "Couldn't save original data."
     fi
+}
+
+# call user check script
+tdx_run_user_check() {
+    TDX_ENC_USER_SCRIPT="/usr/sbin/encryption_allowed"
+    if [ -x ${TDX_ENC_USER_SCRIPT} ]; then
+        if ! [ -O ${TDX_ENC_USER_SCRIPT} -a -G ${TDX_ENC_USER_SCRIPT} ]; then
+            tdx_enc_log "WARNING: Ignoring user check script due to invalid ownership."
+        elif (( 0$(stat -c %a "${TDX_ENC_USER_SCRIPT}") & 07022 )); then
+            tdx_enc_log "WARNING: Ignoring user check script due to invalid permissions."
+        elif ! ${TDX_ENC_USER_SCRIPT}; then
+            tdx_enc_exit_error "Encryption disabled by the user!"
+        fi
+    fi
+}
 
-    tdx_enc_log "Backup created at: ${TDX_ENC_BACKUP_FILE}"
+# unmount the partition if needed ready for encryption
+tdx_enc_pre_unmount() {
+    tdx_enc_log "Unmounting partition ready to encrypt..."
     umount "${TDX_ENC_STORAGE_MOUNTPOINT}"
 }
 
@@ -363,10 +396,14 @@ tdx_enc_partition_remove() {
 
 # mount encrypted partition
 tdx_enc_main_start() {
+    if tdx_enc_pre_mount; then
+        tdx_run_user_check
+        tdx_enc_backup_data
+        tdx_enc_pre_unmount
+    fi
     tdx_enc_prepare_generic
     tdx_enc_prepare_${TDX_ENC_KEY_BACKEND}
     tdx_enc_key_gen_${TDX_ENC_KEY_BACKEND}
-    tdx_enc_backup_data
     tdx_enc_partition_setup
     tdx_enc_partition_mount
     tdx_enc_restore_data


Reviews:
user: login: sergioprado
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-02T09:56:59Z
user: login: thepinkmile
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-02T19:00:34Z
user: login: thepinkmile
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-02T19:16:23Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-03T12:10:16Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-04T18:00:28Z
user: login: sergioprado
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-05T15:37:16Z
user: login: sergioprado
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-05T15:38:41Z
user: login: thepinkmile
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-05T18:19:20Z
user: login: thepinkmile
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-05T18:22:09Z
user: login: rborn-tx
body: Finished a pass.

Overall I think the additions are generally useful to users of this layer, as long as they are "secure". But making them secure doesn't seem to be a big challenge.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-02-24T17:47:09Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-25T09:23:18Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-25T09:54:48Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-25T09:55:50Z
user: login: rborn-tx
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-25T14:49:25Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-26T09:20:13Z
user: login: rborn-tx
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-26T16:59:57Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-26T18:01:23Z
user: login: rborn-tx
body: Overall, LGTM. There's only a missing update to the docs.

I'll ask Sergio to do another pass as well.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T20:24:54Z
user: login: MMG-DG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T15:21:25Z
user: login: rborn-tx
body: LGTM
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-21T20:24:47Z

Review comments:
diff_hunk: @@ -32,6 +32,7 @@ TDX_TEZI_DATA_PARTITION_MOUNT_FLAGS ?= "rw,nosuid,nodev,noatime,errors=remount-r
 TEZI_DATA_ENABLED = "1"
 TEZI_DATA_FSTYPE = "${TDX_TEZI_DATA_PARTITION_TYPE}"
 TEZI_DATA_LABEL = "${TDX_TEZI_DATA_PARTITION_LABEL}"
+TEZI_DATA_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: classes/tdx-tezi-data-partition.bbclass
user: login: sergioprado
body: What is the pporpose of this variable? I cannot see you using it anywhere in the implementation.
created_at: 2025-02-02T09:56:59Z
author_association: COLLABORATOR
diff_hunk: @@ -32,6 +32,7 @@ TDX_TEZI_DATA_PARTITION_MOUNT_FLAGS ?= "rw,nosuid,nodev,noatime,errors=remount-r
 TEZI_DATA_ENABLED = "1"
 TEZI_DATA_FSTYPE = "${TDX_TEZI_DATA_PARTITION_TYPE}"
 TEZI_DATA_LABEL = "${TDX_TEZI_DATA_PARTITION_LABEL}"
+TEZI_DATA_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: classes/tdx-tezi-data-partition.bbclass
user: login: thepinkmile
body: Hmm, I can probably remove that for now. It may be something useful later.
In my build I add to the BSP layer an addition that extracts the data partition to automatically set the TEZI_DATA_FILES variable.
If this is not set here, then the wrong thing could get extracted unless you use the default ‘/data’ as your mountpoint. Which I currently don’t. My mountpoint is different due to legacy need in the app we deploy.
I have submitted a patch to potentially include this capability in the BSP layer.
Alternatively one would have to do this in their local.conf. However, I think this should be the default behaviour as why would you configure an encrypted data partition with one mountpoint and leave the tezi image.json use a different mountpoint?
created_at: 2025-02-02T19:00:34Z
author_association: NONE
path: docs/README-data-partition.md
user: login: thepinkmile
body: One question here would be….
Should this be wrapped up neatly in a ‘tdx-encrypted-data-partition.bbclass’?
Bringing all the parts together in a clean way so users have less chance to get it wrong?
created_at: 2025-02-02T19:12:58Z
author_association: NONE
diff_hunk: @@ -38,4 +38,5 @@ When the said assumption is true though, having an encrypted data partition woul
 ```
 INHERIT += "tdx-tezi-data-partition tdx-encrypted"
 TDX_ENC_STORAGE_LOCATION = "/dev/<block-device-for-data-partition>"
+TDX_ENC_STORAGE_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: docs/README-data-partition.md
user: login: MMG-DG
body: One question here would be….
Should this be wrapped up neatly in a ‘tdx-encrypted-data-partition.bbclass’?
Bringing all the parts together in a clean way so users have less chance to get it wrong?
created_at: 2025-02-03T12:10:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +32,7 @@ TDX_TEZI_DATA_PARTITION_MOUNT_FLAGS ?= "rw,nosuid,nodev,noatime,errors=remount-r
 TEZI_DATA_ENABLED = "1"
 TEZI_DATA_FSTYPE = "${TDX_TEZI_DATA_PARTITION_TYPE}"
 TEZI_DATA_LABEL = "${TDX_TEZI_DATA_PARTITION_LABEL}"
+TEZI_DATA_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: classes/tdx-tezi-data-partition.bbclass
user: login: MMG-DG
body: BTW, thepinkmile is my home account.

I would be happy to put this change and possibly the one I added a comment to on a different PR if that would be prefered?
created_at: 2025-02-04T18:00:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +32,7 @@ TDX_TEZI_DATA_PARTITION_MOUNT_FLAGS ?= "rw,nosuid,nodev,noatime,errors=remount-r
 TEZI_DATA_ENABLED = "1"
 TEZI_DATA_FSTYPE = "${TDX_TEZI_DATA_PARTITION_TYPE}"
 TEZI_DATA_LABEL = "${TDX_TEZI_DATA_PARTITION_LABEL}"
+TEZI_DATA_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: classes/tdx-tezi-data-partition.bbclass
user: login: sergioprado
body: Based on my understanding, this change relies on a modification submitted to the BSP layer. For this reason, I propose removing it for now, for two main reasons:

- It depends on an unmerged change.
- It is unrelated to the user encryption hook, which is the primary focus of this PR.
created_at: 2025-02-05T15:37:16Z
author_association: COLLABORATOR
diff_hunk: @@ -38,4 +38,5 @@ When the said assumption is true though, having an encrypted data partition woul
 ```
 INHERIT += "tdx-tezi-data-partition tdx-encrypted"
 TDX_ENC_STORAGE_LOCATION = "/dev/<block-device-for-data-partition>"
+TDX_ENC_STORAGE_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: docs/README-data-partition.md
user: login: sergioprado
body: See https://github.com/toradex/meta-toradex-security/pull/89/commits/d25717f49da3aa5e22508df9da903b4b9d72bb58#r1943194799
created_at: 2025-02-05T15:38:41Z
author_association: COLLABORATOR
diff_hunk: @@ -32,6 +32,7 @@ TDX_TEZI_DATA_PARTITION_MOUNT_FLAGS ?= "rw,nosuid,nodev,noatime,errors=remount-r
 TEZI_DATA_ENABLED = "1"
 TEZI_DATA_FSTYPE = "${TDX_TEZI_DATA_PARTITION_TYPE}"
 TEZI_DATA_LABEL = "${TDX_TEZI_DATA_PARTITION_LABEL}"
+TEZI_DATA_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: classes/tdx-tezi-data-partition.bbclass
user: login: thepinkmile
body: @sergioprado,
Maybe my comment has been misinterpreted.
My other change (for the BSP) relies on this one. Not the other way around.
This change is something I would expect to already happen as this is just setting the data partition mountpoint configuration to be the same as the one from the tezi-data-partition class here.
Would be happy to make it a separate PR though if preferred.
created_at: 2025-02-05T18:19:20Z
author_association: NONE
diff_hunk: @@ -38,4 +38,5 @@ When the said assumption is true though, having an encrypted data partition woul
 ```
 INHERIT += "tdx-tezi-data-partition tdx-encrypted"
 TDX_ENC_STORAGE_LOCATION = "/dev/<block-device-for-data-partition>"
+TDX_ENC_STORAGE_MOUNTPOINT = "${TDX_TEZI_DATA_PARTITION_MOUNTPOINT}"
path: docs/README-data-partition.md
user: login: thepinkmile
body: @sergioprado,
See my other response…
I think this should be the expected config as again all the mount points should match if using them together as implied here.
created_at: 2025-02-05T18:22:09Z
author_association: NONE
diff_hunk: @@ -92,6 +92,10 @@ Be aware that, if you have a device with a partition encrypted with the test key
 
 To workaround this issue, you can manually remove the key blob, which is by default located at `/var/local/private/.keys/tdx-enc-key.blob`. After removing the key blob and rebooting the device, another key will be generated and the partition will be formatted and encrypted with the new key. As a consequence, you will lose any content on the partition previously encrypted with the test key.
 
+Alternativley, you can provide a script located at `/usr/sbin/encryption_allowed`, which will be used to trigger an `Encryption disabled by the user!` error and exit after initially mounting the partition (this will only occur if the partition has not already been encrypted). This could be used to check the system state, for example reading the SEC_BOOT eFuse (in the case of NXP iMX SoCs) and only enabling encryption once the OTPMK is used, thus avoiding issues when using the initial test key.
+
+NOTE: This user script must be owned by `root` and have the linux file permissions of `0500` to ensure priviliged access and execution requierments are met.
+
path: docs/README-encryption.md
user: login: rborn-tx
body: Alternativley -> Alternatively
linux -> Linux
priviliged -> privileged
requierments -> requirements

created_at: 2025-02-24T16:03:00Z
author_association: COLLABORATOR
diff_hunk: @@ -260,15 +260,28 @@ tdx_enc_key_gen_tpm() {
                               "load \$(cat ${TDX_ENC_KEY_FULLPATH})"
 }
 
-# backup original data in partition (if not encrypted)
-tdx_enc_backup_data() {
-    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
-        tdx_enc_log "Data preservation is not enabled"
+# initially mount the partition if possible
+tdx_enc_pre_mount() {
+    tdx_enc_log "Attempting partition pre-mount"
+
+    TDX_STORAGE_MOUNTED=$(cat /proc/mounts | grep "${TDX_ENC_STORAGE_LOCATION}" | grep "${TDX_ENC_STORAGE_MOUNTPOINT}")
+    if ! [ -z "$TDX_STORAGE_MOUNTED" ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: rborn-tx
body: Is there a valid case where anything other than `${TDX_ENC_STORAGE_LOCATION}` could be mounted to `${TDX_ENC_STORAGE_MOUNTPOINT}`?

If not, which I believe to be the case, then this could be simplified and made more reliable by doing:
```
if mountpoint "${TDX_ENC_STORAGE_MOUNTPOINT}" >/dev/null; then
```

Note: to check non-empty value use `[ -n "$TDX_STORAGE_MOUNTED"]`

created_at: 2025-02-24T17:18:31Z
author_association: COLLABORATOR
diff_hunk: @@ -282,8 +295,23 @@ tdx_enc_backup_data() {
     if [ "$?" -ne 0 ] || echo "${msgs}" | grep -qi 'error\|invalid'; then
         tdx_enc_exit_error "Couldn't save original data."
     fi
+}
 
-    tdx_enc_log "Backup created at: ${TDX_ENC_BACKUP_FILE}"
+# call user check script
+tdx_run_user_check() {
+    TDX_ENC_USER_SCRIPT="/usr/sbin/encryption_allowed"
+    if [ -x ${TDX_ENC_USER_SCRIPT} ]; then
+        if [ "$(stat -c %a ${TDX_ENC_USER_SCRIPT})" != 500 ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: rborn-tx
body: In my understanding, what we need here is to ensure that:

- The script is owned by root (so nobody else can change its permissions).
- The script is not writable by anyone other than root (so nobody else can change its contents (as those contents would be executed with root privileges)).

The current checks would allow a binary like this:

```
-rwx------ 1 attacker attacker 11501 Sep  8  2022 encryption_allowed
```

to be executed which is bad because it would only require the "attacker" user to be able to write to the `/usr/sbin` directory.

created_at: 2025-02-24T17:46:42Z
author_association: COLLABORATOR
diff_hunk: @@ -260,15 +260,28 @@ tdx_enc_key_gen_tpm() {
                               "load \$(cat ${TDX_ENC_KEY_FULLPATH})"
 }
 
-# backup original data in partition (if not encrypted)
-tdx_enc_backup_data() {
-    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
-        tdx_enc_log "Data preservation is not enabled"
+# initially mount the partition if possible
+tdx_enc_pre_mount() {
+    tdx_enc_log "Attempting partition pre-mount"
+
+    TDX_STORAGE_MOUNTED=$(cat /proc/mounts | grep "${TDX_ENC_STORAGE_LOCATION}" | grep "${TDX_ENC_STORAGE_MOUNTPOINT}")
+    if ! [ -z "$TDX_STORAGE_MOUNTED" ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: MMG-DG
body: > Is there a valid case where anything other than `${TDX_ENC_STORAGE_LOCATION}` could be mounted to `${TDX_ENC_STORAGE_MOUNTPOINT}`?

Yes. In the case the partition has already been encrypted and mounted. I have seen that in some cases this script may get executed multiple times.

I believe it may be due to something triggering the remount of rootfs, but I could be wrong.
My suspicion is in my case it may be caused by the fact I use overlay-etc due to now having a read-only rootfs.

We should keep this to ensure we don't continue if what is mounted is not the raw data partition.

I will modify the changes to use `-n` instead of `-z` though and see what happens.
created_at: 2025-02-25T09:23:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -92,6 +92,10 @@ Be aware that, if you have a device with a partition encrypted with the test key
 
 To workaround this issue, you can manually remove the key blob, which is by default located at `/var/local/private/.keys/tdx-enc-key.blob`. After removing the key blob and rebooting the device, another key will be generated and the partition will be formatted and encrypted with the new key. As a consequence, you will lose any content on the partition previously encrypted with the test key.
 
+Alternativley, you can provide a script located at `/usr/sbin/encryption_allowed`, which will be used to trigger an `Encryption disabled by the user!` error and exit after initially mounting the partition (this will only occur if the partition has not already been encrypted). This could be used to check the system state, for example reading the SEC_BOOT eFuse (in the case of NXP iMX SoCs) and only enabling encryption once the OTPMK is used, thus avoiding issues when using the initial test key.
+
+NOTE: This user script must be owned by `root` and have the linux file permissions of `0500` to ensure priviliged access and execution requierments are met.
+
path: docs/README-encryption.md
user: login: MMG-DG
body: Updated
created_at: 2025-02-25T09:54:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -282,8 +295,23 @@ tdx_enc_backup_data() {
     if [ "$?" -ne 0 ] || echo "${msgs}" | grep -qi 'error\|invalid'; then
         tdx_enc_exit_error "Couldn't save original data."
     fi
+}
 
-    tdx_enc_log "Backup created at: ${TDX_ENC_BACKUP_FILE}"
+# call user check script
+tdx_run_user_check() {
+    TDX_ENC_USER_SCRIPT="/usr/sbin/encryption_allowed"
+    if [ -x ${TDX_ENC_USER_SCRIPT} ]; then
+        if [ "$(stat -c %a ${TDX_ENC_USER_SCRIPT})" != 500 ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: MMG-DG
body: Updated to include extra checks for ownership and group for executing user (which should only be `root`).
created_at: 2025-02-25T09:55:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -260,15 +260,28 @@ tdx_enc_key_gen_tpm() {
                               "load \$(cat ${TDX_ENC_KEY_FULLPATH})"
 }
 
-# backup original data in partition (if not encrypted)
-tdx_enc_backup_data() {
-    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
-        tdx_enc_log "Data preservation is not enabled"
+# initially mount the partition if possible
+tdx_enc_pre_mount() {
+    tdx_enc_log "Attempting partition pre-mount"
+
+    TDX_STORAGE_MOUNTED=$(cat /proc/mounts | grep "${TDX_ENC_STORAGE_LOCATION}" | grep "${TDX_ENC_STORAGE_MOUNTPOINT}")
+    if ! [ -z "$TDX_STORAGE_MOUNTED" ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: rborn-tx
body: You could use `findmnt` rather than parsing `/proc/mounts` (I find that parsing a bit brittle):

```bash
if source=$(findmnt -no SOURCE "${TDX_ENC_STORAGE_MOUNTPOINT}"); then
    if [ "${source}" = "${TDX_ENC_STORAGE_LOCATION}" ]; then
        echo "Already mounted"
    fi
fi
```

created_at: 2025-02-25T14:49:25Z
author_association: COLLABORATOR
diff_hunk: @@ -260,15 +260,28 @@ tdx_enc_key_gen_tpm() {
                               "load \$(cat ${TDX_ENC_KEY_FULLPATH})"
 }
 
-# backup original data in partition (if not encrypted)
-tdx_enc_backup_data() {
-    if [ ${TDX_ENC_PRESERVE_DATA} -ne 1 ]; then
-        tdx_enc_log "Data preservation is not enabled"
+# initially mount the partition if possible
+tdx_enc_pre_mount() {
+    tdx_enc_log "Attempting partition pre-mount"
+
+    TDX_STORAGE_MOUNTED=$(cat /proc/mounts | grep "${TDX_ENC_STORAGE_LOCATION}" | grep "${TDX_ENC_STORAGE_MOUNTPOINT}")
+    if ! [ -z "$TDX_STORAGE_MOUNTED" ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: MMG-DG
body: Suggested change has been applied and I am currently testing it on my latest image.
created_at: 2025-02-26T09:20:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -282,8 +295,23 @@ tdx_enc_backup_data() {
     if [ "$?" -ne 0 ] || echo "${msgs}" | grep -qi 'error\|invalid'; then
         tdx_enc_exit_error "Couldn't save original data."
     fi
+}
 
-    tdx_enc_log "Backup created at: ${TDX_ENC_BACKUP_FILE}"
+# call user check script
+tdx_run_user_check() {
+    TDX_ENC_USER_SCRIPT="/usr/sbin/encryption_allowed"
+    if [ -x ${TDX_ENC_USER_SCRIPT} ]; then
+        if [ "$(stat -c %a ${TDX_ENC_USER_SCRIPT})" != 500 ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: rborn-tx
body: A permission of 0500 is quite restrictive. Most scripts in Yocto are installed with permission 0755 which is perfectly secure.

As I said all we need is to ensure is that the file is not writable by others. Additionally we can also check if the suid/sgid/stick bits are set just in case.

I imagine something like this should do the trick:

```
if (( 0$(stat -c %a "${TDX_ENC_USER_SCRIPT}") & 07022 )); then
    echo "Bad perms";
fi
```

Please be sure to test it.

created_at: 2025-02-26T16:59:57Z
author_association: COLLABORATOR
diff_hunk: @@ -282,8 +295,23 @@ tdx_enc_backup_data() {
     if [ "$?" -ne 0 ] || echo "${msgs}" | grep -qi 'error\|invalid'; then
         tdx_enc_exit_error "Couldn't save original data."
     fi
+}
 
-    tdx_enc_log "Backup created at: ${TDX_ENC_BACKUP_FILE}"
+# call user check script
+tdx_run_user_check() {
+    TDX_ENC_USER_SCRIPT="/usr/sbin/encryption_allowed"
+    if [ -x ${TDX_ENC_USER_SCRIPT} ]; then
+        if [ "$(stat -c %a ${TDX_ENC_USER_SCRIPT})" != 500 ]; then
path: recipes-core/tdx-enc-handler/tdx-enc-handler/tdx-enc.sh
user: login: MMG-DG
body: Manual testing of this looks good and happy that this is more robust than what I had before too.
created_at: 2025-02-26T18:01:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -92,6 +92,10 @@ Be aware that, if you have a device with a partition encrypted with the test key
 
 To workaround this issue, you can manually remove the key blob, which is by default located at `/var/local/private/.keys/tdx-enc-key.blob`. After removing the key blob and rebooting the device, another key will be generated and the partition will be formatted and encrypted with the new key. As a consequence, you will lose any content on the partition previously encrypted with the test key.
 
+Alternatively, you can provide a script located at `/usr/sbin/encryption_allowed`, which will be used to trigger an `Encryption disabled by the user!` error and exit after initially mounting the partition (this will only occur if the partition has not already been encrypted). This could be used to check the system state, for example reading the SEC_BOOT eFuse (in the case of NXP iMX SoCs) and only enabling encryption once the OTPMK is used, thus avoiding issues when using the initial test key.
+
+NOTE: This user script must be owned by `root` and have the Linux file permissions of `0500` to ensure privileged access and execution requirements are met.
+
path: docs/README-encryption.md
user: login: rborn-tx
body: This needs to be updated to reflect the code changes.
created_at: 2025-03-05T20:23:38Z
author_association: COLLABORATOR
diff_hunk: @@ -92,6 +92,10 @@ Be aware that, if you have a device with a partition encrypted with the test key
 
 To workaround this issue, you can manually remove the key blob, which is by default located at `/var/local/private/.keys/tdx-enc-key.blob`. After removing the key blob and rebooting the device, another key will be generated and the partition will be formatted and encrypted with the new key. As a consequence, you will lose any content on the partition previously encrypted with the test key.
 
+Alternatively, you can provide a script located at `/usr/sbin/encryption_allowed`, which will be used to trigger an `Encryption disabled by the user!` error and exit after initially mounting the partition (this will only occur if the partition has not already been encrypted). This could be used to check the system state, for example reading the SEC_BOOT eFuse (in the case of NXP iMX SoCs) and only enabling encryption once the OTPMK is used, thus avoiding issues when using the initial test key.
+
+NOTE: This user script must be owned by `root` and have the Linux file permissions of `0500` to ensure privileged access and execution requirements are met.
+
path: docs/README-encryption.md
user: login: MMG-DG
body: Note updated. Hopefully the working is more accurate now.
created_at: 2025-03-21T15:21:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: sergioprado
created_at: 2025-01-30T12:49:30Z
body: @MMG-DG , thanks for the contribution! I haven't done an in-depth review of the implementation yet, but I do have some general feedback to guide the next steps:

- Move the core implementation into a separate class and script. Since this is specific to i.MX SoCs, I want to avoid adding platform-specific code directly to `tdx-encrypted.bbclass` and `tdx-enc.sh`, as these are used across other platforms like TI AM6X.
- Ensure the implementation is flexible enough to support all i.MX SoCs (i.MX6/7/8/9). This will make future porting and maintenance easier. I'm happy to assist with testing across different SoCs to validate compatibility. You may find the OP-TEE source code useful for inspiration: https://github.com/nxp-imx/imx-optee-os/blob/lf-6.6.3_1.0.0/core/drivers/imx_snvs.c#L162C6-L162C28
- Improve the commit message by not only describing what changes are being made but also explaining why they are necessary. This helps reviewers and maintainers understand the context and motivation behind your work.

Thanks!
author_association: COLLABORATOR
user: login: MMG-DG
created_at: 2025-01-30T13:36:25Z
body: Hi @sergioprado,

> * Move the core implementation into a separate class and script.

Is there a way the `tdx-encryption` class and `tdx-enc-handler` script can reference these if it is in it's own "recipe"? Not sure how that would work as the handler script is where the process determines if it needs to skip doing the encryption.

I believe I have updated the main PR comment above with more detail. Is that what you meant? Or do you mean the actual git commit message? Maybe I can update that to point to this PR instead for the detail as there is too much to put in a commit message to do it justice.

I only have access to the imx8 verdin SoM here and I have tested with these ones. However, as part of the issue discussions I am sure one of the other sets of modules uses the same details (as that is where I got the info for the fuse path from). However, I don't have any other iMX SoMs or the data sheets to check. Not to mention the path to the eFuse data is not something that is specified (if I remember correctly) in the data sheets.
author_association: CONTRIBUTOR
user: login: MMG-DG
created_at: 2025-01-30T16:43:04Z
body: @sergioprado

I have just been wondering if it would be easier to add some machine configs that add some variables for the relevant paths and indexes. Then I could add a recipe for a script that only gets included if they exist (probably by adding a MACHINEOVERRIDE tag). Then I can make that a one-time service similar to tdx-enc-handler and make sure it has a definition that makes tdx-enc-handler depend on it (if it is possible to do it that way?).

That would potentially make it separate and machine dependant, and the only affect on on the handler would be to check the existence of a SEC_BOOT file under `/run` to enable the skipping logic.
If the file exists SEC_BOOT is enabled, otherwise it just does what it does now.

Then to add support for a new machine you just add the required machine config?
author_association: CONTRIBUTOR
user: login: MMG-DG
created_at: 2025-01-31T08:27:50Z
body: @sergioprado,

Sorry for all the questions and comments.
What I think I am going to look into is adding the main additions to the nxp dynamic layer so it will only ever be included for nxp based builds.
Then later (if needed), the same thing could be implemented for other SoCs in a relatively simple way.
I was just wondering if I should add something to the SanityChecks for the tdx-encrypted class so that an error shows if the OTPMK flag is set but without the SoC/SoM support for it?
author_association: CONTRIBUTOR
user: login: MMG-DG
created_at: 2025-01-31T12:36:28Z
body: NOTE:
For my latest local testing, I have created some patches for the meta-toradex-bsp-common repo. However I am not sure how to submit them officially. I have posted a question on the community forum and added my patches there (see: [is-there-an-easy-way-to-create-a-dual-rootfs-tezi-image](https://community.toradex.com/t/is-there-an-easy-way-to-create-a-dual-rootfs-tezi-image/26534/5)).
I have currently used the fact that these exist in my local branch of changes.
If that is not wanted I could likely pull those out into this layer instead and re-submit a different PR for the changes?

author_association: CONTRIBUTOR
user: login: sergioprado
created_at: 2025-02-01T16:06:34Z
body: @MMG-DG Before diving into the technical details of the implementation, I wanted to take a step back and consider the potential benefits of implementing this in `meta-toradex-security`.

If I understand correctly (please feel free to correct me), the goal is to prevent the encryption script from encrypting a partition using the SoC development keys, and postpone the encryption until after secure boot is enabled, thereby ensuring that the data in the partition isn't lost prematurely.

While I see how this approach could benefit certain users, there are alternative ways to achieve similar results. Given the complexity involved—particularly the testing and maintenance required for each SoC—this might result in a significant amount of work.

What if we took a more generic approach within the layer? For example, we could introduce a snippet like this:

```
if [ -x /usr/sbin/encryption_allowed ]; then
    if ! /usr/sbin/encryption_allowed; then
        tdx_enc_exit_error "Encryption disabled by the user!"
    fi
fi
```

With this setup, you could continue using the encryption script as-is while implementing your custom logic in `/usr/sbin/encryption_allowed`. This script could be included as part of your OS image, giving you the flexibility to control encryption behavior without modifying the core script.

What do you think? Does this approach make sense?

@rborn-tx @jsrc27 I’d also appreciate your thoughts on this.
author_association: COLLABORATOR
user: login: thepinkmile
created_at: 2025-02-01T20:22:58Z
body: @sergioprado 

> What do you think? Does this approach make sense?

I can certainly see where this could go. And I guess that would then mean I just move the rest of my custom changes into my own layer to provide the functionality of checking the values I need.

Initially my first concern was having a way to do this such that it would work with a read-only rootfs.
As the fuse is essentially dynamic as it’s discovered at runtime, I implemented it this way.
However, I can see it would likely require maintenance and extension as SoM support grows.

The only comment I will add is there does need to be a small change to the ordering of existing functions in start_main to ensure correct mount states regardless of “user intervention”.

I will update this on Monday when I am back in the office. Should then be a very simple change for the PR and I can move my complex parts into my own layer.
If I do that I can likely simplify it greatly as I would not need to support SoMs we don’t use.

regards,
author_association: NONE
user: login: sergioprado
created_at: 2025-02-05T16:25:37Z
body: @MMG-DG I was thinking about this, and I am not sure if we should merge this change.

The solution I proposed was to make it possible for the user to cancel the encryption operation via an external script, but I was wondering if this is some kind of backdoor we are introducing.

I would really like some feedback from the other maintainers.

@jsrc27 @rborn-tx any thoughts on this https://github.com/toradex/meta-toradex-security/pull/89#issuecomment-2629008919?


author_association: COLLABORATOR
user: login: thepinkmile
created_at: 2025-02-05T18:27:53Z
body: > @MMG-DG I was thinking about this, and I am not sure if we should merge this change.
> 
> The solution I proposed was to make it possible for the user to cancel the encryption operation via an external script, but I was wondering if this is some kind of backdoor we are introducing.
> 
> I would really like some feedback from the other maintainers.
> 
> @jsrc27 @rborn-tx any thoughts on this [#89 (comment)](https://github.com/toradex/meta-toradex-security/pull/89#issuecomment-2629008919)?

Not sure how it could be a backdoor as it only has any effect when the partition is not encrypted.
So, at least in theory, not a finalised production unit out in the wild.
It is merely a way to delay the encryption process during the manufacturing process.
Another possible way would be to add a user defined dependency on a file in the service (but that is probably the same concern as a script).
Maybe a requirement on the external script could be added to ensure permissions are as restricted as possible before executing it?

possibly adding something like:
‘if [ "$(stat --format %a /usr/sbin/encryption_allowed)" -eq “500” ]; then’
We already know it exists and is executable. This would ensure it can only be read and executed by the current user (e.g. root).
This way to even have the script there in the first place you would have to have elevated permissions.
author_association: NONE
user: login: rborn-tx
created_at: 2025-02-19T12:09:20Z
body: > @jsrc27 @rborn-tx any thoughts on this https://github.com/toradex/meta-toradex-security/pull/89#issuecomment-2629008919?

@sergioprado Sorry, I wasn't able to look into this topic so far. I'm hoping to be able to spend some time on it next week.

author_association: COLLABORATOR
user: login: sergioprado
created_at: 2025-02-19T13:39:56Z
body: > @sergioprado Sorry, I wasn't able to look into this topic so far. I'm hoping to be able to spend some time on it next week.

@rborn-tx No problem at all! I’d love to hear your thoughts on this. Thanks!
author_association: COLLABORATOR
user: login: sergioprado
created_at: 2025-03-22T11:41:22Z
body: @MMG-DG Thanks for your contribution. Looks good to me as well. Merging this now.
author_association: COLLABORATOR
user: login: sergioprado
created_at: 2025-03-22T13:48:25Z
body: Commit cherry-picked to scarthgap: [7f8559c05fed](https://github.com/toradex/meta-toradex-security/commit/df75a658015e5daa278b2ceb2caf36c9ffb06bec)
author_association: COLLABORATOR