diff mbox series

[v2,2/3] common/encrypt: support hardware-wrapped key testing

Message ID 20241213052840.314921-3-ebiggers@kernel.org (mailing list archive)
State New
Headers show
Series xfstests: test the fscrypt hardware-wrapped key support | expand

Commit Message

Eric Biggers Dec. 13, 2024, 5:28 a.m. UTC
From: Eric Biggers <ebiggers@google.com>

To support testing the kernel's support for hardware-wrapped inline
encryption keys, update _verify_ciphertext_for_encryption_policy() to
support a hw_wrapped_key option.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/config  |  1 +
 common/encrypt | 80 +++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 77 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/common/config b/common/config
index fcff0660..091405b3 100644
--- a/common/config
+++ b/common/config
@@ -233,10 +233,11 @@  export BLKZONE_PROG="$(type -P blkzone)"
 export GZIP_PROG="$(type -P gzip)"
 export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
 export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
 export PARTED_PROG="$(type -P parted)"
 export XFS_PROPERTY_PROG="$(type -P xfs_property)"
+export FSCRYPTCTL_PROG="$(type -P fscryptctl)"
 
 # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
 # newer systems have udevadm command but older systems like RHEL5 don't.
 # But if neither one is available, just set it to "sleep 1" to wait for lv to
 # be settled
diff --git a/common/encrypt b/common/encrypt
index d90a566a..1caca767 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -150,10 +150,46 @@  _require_encryption_policy_support()
 		$KEYCTL_PROG clear $TEST_KEYRING_ID
 	fi
 	rm -r $dir
 }
 
+# Require that the scratch filesystem accepts the "inlinecrypt" mount option.
+#
+# This does not check whether the scratch block device has any specific inline
+# encryption capabilities.
+_require_scratch_inlinecrypt()
+{
+	_require_scratch
+	_scratch_mkfs &>> $seqres.full
+	if ! _try_scratch_mount -o inlinecrypt &>> $seqres.full; then
+		_notrun "filesystem doesn't support -o inlinecrypt"
+	fi
+}
+
+# Require that the given block device supports hardware-wrapped inline
+# encryption keys, and require that a command-line tool that supports
+# importing/generating/preparing them is available.
+_require_hw_wrapped_key_support()
+{
+	local dev=$1
+
+	echo "Checking for HW-wrapped key support on $dev" >> $seqres.full
+	local sysfs_dir=$(_sysfs_dev $dev)
+	if [ ! -e $sysfs_dir/queue ]; then
+		sysfs_dir=$sysfs_dir/..
+	fi
+	if [ ! -e $sysfs_dir/queue/crypto/hw_wrapped_keys ]; then
+		_notrun "$dev doesn't support hardware-wrapped inline encryption keys"
+	fi
+
+	echo "Checking for fscryptctl support for HW-wrapped keys" >> $seqres.full
+	_require_command "$FSCRYPTCTL_PROG" fscryptctl
+	if ! "$FSCRYPTCTL_PROG" --help | grep -q "import_hw_wrapped_key"; then
+		_notrun "fscryptctl too old; doesn't support hardware-wrapped inline encryption keys"
+	fi
+}
+
 _scratch_mkfs_encrypted()
 {
 	case $FSTYP in
 	ext4|f2fs)
 		_scratch_mkfs -O encrypt
@@ -249,18 +285,21 @@  _generate_key_descriptor()
 }
 
 # Generate a raw encryption key, but don't add it to any keyring yet.
 _generate_raw_encryption_key()
 {
+	local size=${1:-64}
 	local raw=""
 	local i
-	for ((i = 0; i < 64; i++)); do
+	for ((i = 0; i < $size; i++)); do
 		raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))"
 	done
 	echo $raw
 }
 
+RAW_HW_KEY_SIZE=32
+
 # Serialize an integer into a CPU-endian bytestring of the given length, and
 # print it as a string where each byte is hex-escaped.  For example,
 # `_num_to_hex 1000 4` == "\xe8\x03\x00\x00" if the CPU is little endian.
 _num_to_hex()
 {
@@ -405,10 +444,25 @@  _add_enckey()
 	shift 2
 
 	echo -ne "$raw_key" | $XFS_IO_PROG -c "add_enckey $*" "$mnt"
 }
 
+# Create a hardware-wrapped key from the given raw key using the given block
+# device, add it to the given filesystem, and print the resulting key
+# identifier.
+_add_hw_wrapped_key()
+{
+	local dev=$1
+	local mnt=$2
+	local raw_key=$3
+
+	echo -ne "$raw_key" | \
+		$FSCRYPTCTL_PROG import_hw_wrapped_key "$dev" | \
+		$FSCRYPTCTL_PROG prepare_hw_wrapped_key "$dev" | \
+		$FSCRYPTCTL_PROG add_key --hw-wrapped-key "$mnt"
+}
+
 _user_do_add_enckey()
 {
 	local mnt=$1
 	local raw_key=$2
 	shift 2
@@ -851,19 +905,21 @@  _fscrypt_mode_name_to_num()
 #	'v2':			test a v2 encryption policy
 #	'direct':		test the DIRECT_KEY policy flag
 #	'iv_ino_lblk_64':	test the IV_INO_LBLK_64 policy flag
 #	'iv_ino_lblk_32':	test the IV_INO_LBLK_32 policy flag
 #	'log2_dusize=N':        test the log2_data_unit_size field
+#	'hw_wrapped_key':	use a hardware-wrapped inline encryption key
 #
 _verify_ciphertext_for_encryption_policy()
 {
 	local contents_encryption_mode=$1
 	local filenames_encryption_mode=$2
 	local opt
 	local policy_version=1
 	local policy_flags=0
 	local log2_dusize=0
+	local hw_wrapped_key=false
 	local set_encpolicy_args=""
 	local crypt_util_args=""
 	local crypt_util_contents_args=""
 	local crypt_util_filename_args=""
 	local expected_identifier
@@ -888,10 +944,15 @@  _verify_ciphertext_for_encryption_policy()
 			(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 ))
 			;;
 		log2_dusize=*)
 			log2_dusize=$(echo "$opt" | sed 's/^log2_dusize=//')
 			;;
+		hw_wrapped_key)
+			hw_wrapped_key=true
+			crypt_util_args+=" --enable-hw-kdf"
+			crypt_util_contents_args+=" --use-inlinecrypt-key"
+			;;
 		*)
 			_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
 			;;
 		esac
 	done
@@ -927,10 +988,13 @@  _verify_ciphertext_for_encryption_policy()
 		fi
 	fi
 	set_encpolicy_args=${set_encpolicy_args# }
 
 	_require_scratch_encryption $set_encpolicy_args -f $policy_flags
+	if $hw_wrapped_key; then
+		_require_hw_wrapped_key_support $SCRATCH_DEV
+	fi
 	_require_test_program "fscrypt-crypt-util"
 	_require_xfs_io_command "fiemap"
 	_require_get_encryption_nonce_support
 	_require_get_ciphertext_filename_support
 	if (( policy_version == 1 )); then
@@ -956,15 +1020,23 @@  _verify_ciphertext_for_encryption_policy()
 
 	crypt_util_contents_args+="$crypt_util_args"
 	crypt_util_filename_args+="$crypt_util_args"
 
 	echo "Generating encryption key" >> $seqres.full
-	local raw_key=$(_generate_raw_encryption_key)
 	if (( policy_version > 1 )); then
-		local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" \
-				| awk '{print $NF}')
+		if $hw_wrapped_key; then
+			local raw_key=$(_generate_raw_encryption_key \
+					$RAW_HW_KEY_SIZE)
+			local keyspec=$(_add_hw_wrapped_key $SCRATCH_DEV \
+					$SCRATCH_MNT "$raw_key")
+		else
+			local raw_key=$(_generate_raw_encryption_key)
+			local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" | \
+					awk '{print $NF}')
+		fi
 	else
+		local raw_key=$(_generate_raw_encryption_key)
 		local keyspec=$(_generate_key_descriptor)
 		_init_session_keyring
 		_add_session_encryption_key $keyspec $raw_key
 	fi
 	local raw_key_hex=$(echo "$raw_key" | tr -d '\\x')