#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright 2019 Google LLC
#
# FS QA Test No. 593
#
# Test adding a key to a filesystem's fscrypt keyring via an
# "fscrypt-provisioning" keyring key.  This is an alternative to the normal
# method where the raw key is given directly.
#
. ./common/preamble
_begin_fstest auto quick encrypt

# Import common functions.
. ./common/filter
. ./common/encrypt

# real QA test starts here
_supported_fs generic
_require_scratch_encryption -v 2
_require_command "$KEYCTL_PROG" keyctl

_init_session_keyring
_scratch_mkfs_encrypted &>> $seqres.full
_scratch_mount
_require_add_enckey_by_key_id $SCRATCH_MNT

test_with_policy_version()
{
	local vers=$1
	local dir=$SCRATCH_MNT/dir
	local keyid

	echo
	echo "# =========================="
	echo "# Test with policy version $vers"
	echo "# =========================="

	case $vers in
	1)
		local keytype=$FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR
		local keyspec=$TEST_KEY_DESCRIPTOR
		local add_enckey_args="-d $TEST_KEY_DESCRIPTOR"
		;;
	2)
		local keytype=$FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER
		local keyspec=$TEST_KEY_IDENTIFIER
		local add_enckey_args=""
		;;
	*)
		_fail "Unknown policy version: $vers"
		;;
	esac

	# First add the key in the regular way (raw key given directly), create
	# an encrypted file with some contents, and remove the key.  After this,
	# the encrypted file should no longer be readable.

	echo -e "\n# Adding key to filesystem"
	_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" $add_enckey_args

	echo -e "\n# Creating encrypted file"
	mkdir $dir
	_set_encpolicy $dir $keyspec
	echo "contents" > $dir/file

	echo -e "\n# Removing key from filesystem"
	_rm_enckey $SCRATCH_MNT $keyspec
	cat $dir/file |& _filter_scratch

	# Now we should be able to add the key back via an fscrypt-provisioning
	# key which contains the raw key, instead of providing the raw key
	# directly.  After this, the encrypted file should be readable again.

	echo -e "\n# Adding fscrypt-provisioning key"
	keyid=$(_add_fscrypt_provisioning_key $keyspec $keytype "$TEST_RAW_KEY")

	echo -e "\n# Adding key to filesystem via fscrypt-provisioning key"
	$XFS_IO_PROG -c "add_enckey -k $keyid $add_enckey_args" $SCRATCH_MNT

	echo -e "\n# Reading encrypted file"
	cat $dir/file

	echo -e "\n# Cleaning up"
	rm -rf $dir
	_scratch_cycle_mount	# Clear all keys
}

# Test with both v1 and v2 encryption policies.
test_with_policy_version 1
test_with_policy_version 2

# Now test that invalid fscrypt-provisioning keys can't be created, that
# fscrypt-provisioning keys can't be read back by userspace, and that the
# filesystem only accepts properly matching fscrypt-provisioning keys.
echo
echo "# ================"
echo "# Validation tests"
echo "# ================"

echo -e "\n# Adding an invalid fscrypt-provisioning key fails"
echo "# ... bad type"
_add_fscrypt_provisioning_key desc 0 "$TEST_RAW_KEY"
echo "# ... bad type"
_add_fscrypt_provisioning_key desc 10000 "$TEST_RAW_KEY"
echo "# ... raw key too small"
_add_fscrypt_provisioning_key desc $FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR ""
echo "# ... raw key too large"
_add_fscrypt_provisioning_key desc $FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR \
	"$TEST_RAW_KEY$TEST_RAW_KEY"

echo -e "\n# keyctl_read() doesn't work on fscrypt-provisioning keys"
keyid=$(_add_fscrypt_provisioning_key desc $FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR \
	"$TEST_RAW_KEY")
$KEYCTL_PROG read $keyid
$KEYCTL_PROG unlink $keyid $TEST_KEYRING_ID

echo -e "\n# Only keys with the correct fscrypt_provisioning_key_payload::type field can be added"
echo "# ... keyring key is v1, filesystem wants v2 key"
keyid=$(_add_fscrypt_provisioning_key desc $FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR \
	"$TEST_RAW_KEY")
$XFS_IO_PROG -c "add_enckey -k $keyid" $SCRATCH_MNT
$KEYCTL_PROG unlink $keyid $TEST_KEYRING_ID

echo "# ... keyring key is v2, filesystem wants v1 key"
keyid=$(_add_fscrypt_provisioning_key desc $FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER \
	"$TEST_RAW_KEY")
$XFS_IO_PROG -c "add_enckey -k $keyid -d $TEST_KEY_DESCRIPTOR" $SCRATCH_MNT
$KEYCTL_PROG unlink $keyid $TEST_KEYRING_ID

echo -e "\n# Only keys of type fscrypt-provisioning can be added"
keyid=$(head -c 64 /dev/urandom | \
	$KEYCTL_PROG padd logon foo:desc $TEST_KEYRING_ID)
$XFS_IO_PROG -c "add_enckey -k $keyid" $SCRATCH_MNT
$KEYCTL_PROG unlink $keyid $TEST_KEYRING_ID

# success, all done
status=0
exit
