Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
SPDX-License-Identifier: BSD-3-Clause-Clear

# Qualcomm UserDataEncryption Functionality Test Script
## Overview

The `UserDataEncryption` test script verifies basic filesystem encryption functionality. It generates a 64-byte key, adds it to the system, applies an encryption policy to a mount directory, and confirms the setup by creating and reading a test file. This ensures that key management and encryption policies work as expected.

## Features

- **Dependency Check**: Verifies the presence of the `fscryptctl` binary.
- **Key Management**: Generates a 64-byte key and adds it to the filesystem.
- **Encryption Policy**: Applies and verifies encryption policy on a mount directory.
- **Functional Validation**: Creates and reads a test file to confirm encryption functionality.
- **Automated Result Logging**: Outputs test results to a `.res` file for automated result collection.

## Prerequisites

Ensure the following components are present on the target device:

- `fscryptctl` binary available in `/data/`
- Sufficient permissions to create and mount directories

## Directory Structure
```
Runner/
├── suites/
│ ├── Kernel/
│ │ │ ├── baseport/
│ │ │ │ ├── UserDataEncryption/
│ │ │ │ │ ├── run.sh
```
## Usage

1. Copy repo to Target Device: Use scp to transfer the scripts from the host to the target device. The scripts should be copied to the ```/<user-defined-location>``` directory on the target device.

2. Verify Transfer: Ensure that the repo have been successfully copied to the ```/<user-defined-location>``` directory on the target device.

3. Run Scripts: Navigate to the ```/<user-defined-location>``` directory on the target device and execute the scripts as needed.

---
Quick Example
```
git clone <this-repo>
cd <this-repo>
scp -r common Runner user@target_device_ip:/<user-defined-location>
ssh user@target_device_ip
cd /<user-defined-location>/Runner && ./run-test.sh UserDataEncryption

Sample output:
sh-5.2# ./run-test.sh UserDataEncryption
[Executing test case: UserDataEncryption] 2025-12-19 14:30:04 -
[INFO] 2025-12-19 14:30:04 - Running as root. Continuing...
[INFO] 2025-12-19 14:30:04 - -----------------------------------------------------------------------------------------
[INFO] 2025-12-19 14:30:04 - -------------------Starting UserDataEncryption Testcase----------------------------
[INFO] 2025-12-19 14:30:04 - === Test Initialization ===
[INFO] 2025-12-19 14:30:04 - Kernel supports fscrypt (CONFIG_FS_ENCRYPTION=y/m).
[INFO] 2025-12-19 14:30:04 - Checking if dependency binary is available
[INFO] 2025-12-19 14:30:04 - Temporary key file created: /tmp/tmp.mI6nEn6f1x
[INFO] 2025-12-19 14:30:04 - Generating 64-byte encryption key
[INFO] 2025-12-19 14:30:04 - Creating unique mount folder under /mnt
[INFO] 2025-12-19 14:30:04 - Created unique mount directory: /mnt/testing.XIPMnd
[INFO] 2025-12-19 14:30:04 - Derived filesystem mount point: /var
[INFO] 2025-12-19 14:30:04 - Filesystem at /var: ext4
[INFO] 2025-12-19 14:30:04 - Adding encryption key to the filesystem
[INFO] 2025-12-19 14:30:04 - No relevant, non-benign errors for modules [fscrypt] in recent dmesg.
[INFO] 2025-12-19 14:30:04 - Key ID: 93c4e82e13a0d4472cb48a498047db57
[INFO] 2025-12-19 14:30:04 - Checking key status
[INFO] 2025-12-19 14:30:04 - Key Status: Present (user_count=1, added_by_self)
[INFO] 2025-12-19 14:30:04 - Setting encryption policy on /mnt/testing.XIPMnd
[INFO] 2025-12-19 14:30:04 - No relevant, non-benign errors for modules [fscrypt] in recent dmesg.
[INFO] 2025-12-19 14:30:04 - Verifying encryption policy
[INFO] 2025-12-19 14:30:04 - No relevant, non-benign errors for modules [fscrypt] in recent dmesg.
[INFO] 2025-12-19 14:30:04 - Policy verification successful: Master key identifier matches key_id
[INFO] 2025-12-19 14:30:04 - Creating test file in encrypted directory
[INFO] 2025-12-19 14:30:04 - Reading test file
[PASS] 2025-12-19 14:30:04 - UserDataEncryption : Test Passed
[INFO] 2025-12-19 14:30:04 - -------------------Completed UserDataEncryption Testcase----------------------------
[INFO] 2025-12-19 14:30:04 - Cleaning up mount directory: /mnt/testing.XIPMnd
[INFO] 2025-12-19 14:30:04 - Deleted test file: /mnt/testing.XIPMnd/file.txt
[INFO] 2025-12-19 14:30:04 - Removed mount directory: /mnt/testing.XIPMnd
[PASS] 2025-12-19 14:30:04 - UserDataEncryption passed

[INFO] 2025-12-19 14:30:04 - ========== Test Summary ==========
PASSED:
UserDataEncryption

FAILED:
None

SKIPPED:
None
[INFO] 2025-12-19 14:30:04 - ==================================
4. Results will be available in the `/<user-defined-location>/Runner/suites/Kernel/baseport/UserDataEncryption/` directory.

## Notes

- The script uses /data/UserDataEncryption for all operations.
- Temporary files such as the encryption key are cleaned up after the test.
- If any test fails, the script logs the error and exits with a failure code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
metadata:
format: Lava-Test Test Definition 1.0
name: UserDataEncryption
description: "Validates fscrypt-based user data encryption on supported filesystems"
maintainer:
- bbharani@qti.qualcomm.com
os:
- openembedded
scope:
- functional
devices:
- rb3gen2
- ridesx
- lemans evk
- monaco evk

run:
steps:
- cd Runner
- $PWD/suites/Kernel/Baseport/UserDataEncryption/run.sh || true
- $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/UserDataEncryption/UserDataEncryption.res || true
236 changes: 236 additions & 0 deletions Runner/suites/Kernel/Baseport/UserDataEncryption/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#!/bin/sh

# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
# SPDX-License-Identifier: BSD-3-Clause-Clear
# Robustly find and source init_env
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
INIT_ENV=""
SEARCH="$SCRIPT_DIR"
while [ "$SEARCH" != "/" ]; do
if [ -f "$SEARCH/init_env" ]; then
INIT_ENV="$SEARCH/init_env"
break
fi
SEARCH=$(dirname "$SEARCH")
done

if [ -z "$INIT_ENV" ]; then
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
exit 0
fi

if [ -z "$__INIT_ENV_LOADED" ]; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the var is unset, some shells can still behave oddly, also it will trigger set -u issues if added later.
Use:
if [ -z "${__INIT_ENV_LOADED:-}" ]; then

# shellcheck disable=SC1090
. "$INIT_ENV"
fi

# shellcheck disable=SC1090,SC1091
. "$TOOLS/functestlib.sh"

FSCRYPTCTL="${FSCRYPTCTL:-fscryptctl}"
TESTNAME="UserDataEncryption"
test_path=$(find_test_case_by_name "$TESTNAME")

if [ -z "$test_path" ]; then
log_fail "Path not found for $TESTNAME test. Falling back to SCRIPT_DIR: $SCRIPT_DIR"
test_path="$SCRIPT_DIR"
fi

res_file="$test_path/$TESTNAME.res"

# Ensure script runs as root
if [ "$(id -u)" -ne 0 ]; then
log_skip "This script must be run as root."
echo "$TESTNAME SKIP" > "$res_file"
exit 0
else
log_info "Running as root. Continuing..."
fi

if ! cd "$test_path"; then
log_fail "Failed to change directory to $test_path"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi

# Globals that cleanup will use
MOUNT_DIR=""
FS_PATH=""
key_id=""
KEY_FILE=""

cleanup() {
if [ -n "$MOUNT_DIR" ] && [ "$MOUNT_DIR" != "/" ]; then
log_info "Cleaning up mount directory: $MOUNT_DIR"

if rm -f "$MOUNT_DIR/file.txt" 2>/dev/null; then
log_info "Deleted test file: $MOUNT_DIR/file.txt"
else
log_warn "Failed to delete test file: $MOUNT_DIR/file.txt"
fi

if rmdir "$MOUNT_DIR" 2>/dev/null; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm / rmdir cleanup is fine, but you should not assume the file exists. Your logging is OK, but make it quieter.
Remove the “failed to delete” warnings when the file simply doesn’t exist.
Example:
[ -f "$MOUNT_DIR/file.txt" ] && rm -f "$MOUNT_DIR/file.txt" ...

log_info "Removed mount directory: $MOUNT_DIR"
else
log_warn "Failed to remove mount directory: $MOUNT_DIR"
fi
fi

if [ -n "$key_id" ]; then
"$FSCRYPTCTL" remove_key "$key_id" "$FS_PATH" >/dev/null 2>&1 || true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup removes key even if FS_PATH is empty. If key_id is set but FS_PATH is empty (partial failure), you may call remove_key with bad args.

fi


if [ -n "$KEY_FILE" ]; then
if ! rm -f "$KEY_FILE" 2>/dev/null; then
log_warn "Failed to remove key file: $KEY_FILE"
fi
fi
}

# Run cleanup on normal exit, Ctrl-C, or SIGTERM
trap cleanup EXIT INT TERM

log_info "-----------------------------------------------------------------------------------------"
log_info "-------------------Starting $TESTNAME Testcase----------------------------"
log_info "=== Test Initialization ==="


## kernel config check
if [ -r /proc/config.gz ]; then
if zcat /proc/config.gz | grep -q '^CONFIG_FS_ENCRYPTION=[ym]'; then
log_info "Kernel supports fscrypt (CONFIG_FS_ENCRYPTION=y/m)."
else
log_skip "$TESTNAME : Kernel lacks CONFIG_FS_ENCRYPTION. Skipping."
echo "$TESTNAME SKIP" > "$res_file"
exit 0
fi
else
log_warn "/proc/config.gz not available; cannot verify kernel config. Proceeding"
fi

log_info "Checking if dependency binary is available"
check_dependencies "$FSCRYPTCTL"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if check_dependencies returns non-zero you currently don’t handle it (you just call it).
Better to have an safer pattern.


# Create a secure temporary file for the key
if KEY_FILE="$(mktemp)"; then
log_info "Temporary key file created: $KEY_FILE"
chmod 600 "$KEY_FILE"
else
log_fail "$TESTNAME : Failed to create temporary key file"
echo "[ERROR] Failed to create temporary key file" >&2
exit 0
fi

# Step 1: Generate a 64-byte key
log_info "Generating 64-byte encryption key"
if ! head -c 64 /dev/urandom > "$KEY_FILE"; then
log_fail "$TESTNAME : Failed to generate encryption key"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi

# Step 2: Create mount folder (this will create an unique folder under mnt)
log_info "Creating unique mount folder under /mnt"
MOUNT_DIR=$(mktemp -d /mnt/testing.XXXXXX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mktemp under /mnt is not guaranteed to exist or be writable. On many images /mnt exists, but not always. Better:
Prefer ${TMPDIR:-/tmp} or ensure /mnt exists.
Example:
BASE_TMP="${TMPDIR:-/tmp}"
MOUNT_DIR="$(mktemp -d "$BASE_TMP/testing.XXXXXX")" || ...

if [ ! -d "$MOUNT_DIR" ]; then
log_fail "$TESTNAME : Failed to create mount directory"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi
log_info "Created unique mount directory: $MOUNT_DIR"


FS_PATH=$(df --output=target "$MOUNT_DIR" | tail -n 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there's any chance of a busybox/ non-GNU environment, just be aware --output=target may not exist. Given the meta-qcom target, it's probably fine. If we want to be ultra-portable in future, this part might be abstracted into hlper, but for now it's acceptable.

if [ -z "$FS_PATH" ]; then
log_fail "$TESTNAME : Failed to determine filesystem mount point for $MOUNT_DIR"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi
log_info "Derived filesystem mount point: $FS_PATH"


#file-system check
if fs_type="$(df -Th "$FS_PATH" 2>/dev/null | awk 'NR==2{print $2}')"; then
if [ -n "$fs_type" ]; then
log_info "Filesystem at $FS_PATH: $fs_type"
else
log_warn "df -Th succeeded but could not parse filesystem type for $FS_PATH"
fi
else
log_warn "df -Th failed for $FS_PATH"
fi

# Step 3: Add the key to the filesystem
log_info "Adding encryption key to the filesystem"
key_id=$("$FSCRYPTCTL" add_key "$FS_PATH" < "$KEY_FILE" 2>/dev/null)
scan_dmesg_errors "$SCRIPT_DIR" "fscrypt" ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scan_dmesg_errors is aggressive, consider only scan at end, or scan with a narrow time window.


if [ -z "$key_id" ]; then
log_fail "$TESTNAME : Failed to add encryption key"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi

log_info "Key ID: $key_id"

# Step 4: Check key status
log_info "Checking key status"
status=$("$FSCRYPTCTL" key_status "$key_id" "$FS_PATH" 2>/dev/null)
if [ -z "$status" ]; then
log_fail "$TESTNAME : Failed to get key status"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi
log_info "Key Status: $status"

if ! echo "$status" | grep -q "^Present"; then
log_fail "$TESTNAME : Key is not usable (status: $status)"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi


# Step 5: Set encryption policy
log_info "Setting encryption policy on $MOUNT_DIR"

if ! "$FSCRYPTCTL" set_policy "$key_id" "$MOUNT_DIR"; then
log_fail "$TESTNAME : Failed to set encryption policy"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi
scan_dmesg_errors "$SCRIPT_DIR" "fscrypt" ""


# Step 6: Verify policy
log_info "Verifying encryption policy"
policy_output=$("$FSCRYPTCTL" get_policy "$MOUNT_DIR" 2>/dev/null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your “encryption validation” is too weak (it will pass even if nothing is encrypted)
Right now you:

  • set policy on an empty directory
  • write a file
  • read it back

That proves “filesystem writable”, not “data is encrypted”.

At minimum add one real validation:
Confirm the directory is marked encrypted via fscryptctl/lsattr output, OR
Check that get_policy returns non-empty + matches key id (you already do), and that encryption flags exist.

scan_dmesg_errors "$SCRIPT_DIR" "fscrypt" ""


policy_key=$(echo "$policy_output" | awk -F': ' '/Master key identifier/ {print $2}' | tr -d '[:space:]')

if [ "$policy_key" = "$key_id" ]; then
log_info "Policy verification successful: Master key identifier matches key_id"
else
log_fail "$TESTNAME : Policy verification failed (expected $key_id, got $policy_key)"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi

# Step 7: Create and read a test file
log_info "Creating test file in encrypted directory"
echo "file" > "$MOUNT_DIR/file.txt"

log_info "Reading test file"
file_content=$(cat "$MOUNT_DIR/file.txt")
if [ "$file_content" = "file" ]; then
log_pass "$TESTNAME : Test Passed"
echo "$TESTNAME PASS" > "$res_file"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add exit 0

else
log_fail "$TESTNAME : Test Failed"
echo "$TESTNAME FAIL" > "$res_file"
exit 0
fi

log_info "-------------------Completed $TESTNAME Testcase----------------------------"
Loading