generic: test adding filesystem-level fscrypt key via key_id

Add a test which tests 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.

For more details, see kernel commit 93edd392cad7 ("fscrypt: support
passing a keyring key to FS_IOC_ADD_ENCRYPTION_KEY").

This test depends on an xfs_io patch which adds the '-k' option to the
'add_enckey' command, e.g.:

	xfs_io -c "add_enckey -k KEY_ID" MOUNTPOINT

This test is skipped if the needed kernel or xfs_io support is absent.

This has been tested on ext4, f2fs, and ubifs.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
This commit is contained in:
Eric Biggers
2020-02-03 10:18:55 -08:00
committed by Eryu Guan
parent 0d9ca511e1
commit 0ea2b67b09
4 changed files with 299 additions and 14 deletions
+70 -14
View File
@@ -227,6 +227,28 @@ _generate_raw_encryption_key()
echo $raw
}
# 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()
{
local value=$1
local nbytes=$2
local i
local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
cut -f2 -d' ' | cut -c1)
if (( big_endian )); then
for (( i = 0; i < nbytes; i++ )); do
printf '\\x%02x' $(((value >> (8*(nbytes-1-i))) & 0xff))
done
else
for (( i = 0; i < nbytes; i++ )); do
printf '\\x%02x' $(((value >> (8*i)) & 0xff))
done
fi
}
# Add the specified raw encryption key to the session keyring, using the
# specified key descriptor.
_add_session_encryption_key()
@@ -237,12 +259,12 @@ _add_session_encryption_key()
#
# Add the key to the session keyring. The required structure is:
#
# #define FS_MAX_KEY_SIZE 64
# #define FSCRYPT_MAX_KEY_SIZE 64
# struct fscrypt_key {
# u32 mode;
# u8 raw[FS_MAX_KEY_SIZE];
# u32 size;
# } __packed;
# __u32 mode;
# __u8 raw[FSCRYPT_MAX_KEY_SIZE];
# __u32 size;
# };
#
# The kernel ignores 'mode' but requires that 'size' be 64.
#
@@ -253,15 +275,8 @@ _add_session_encryption_key()
# nice to use the common key prefix, but for now use the filesystem-
# specific prefix to make it possible to test older kernels...
#
local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
cut -f2 -d' ' | cut -c1 )
if (( big_endian )); then
local mode='\x00\x00\x00\x00'
local size='\x00\x00\x00\x40'
else
local mode='\x00\x00\x00\x00'
local size='\x40\x00\x00\x00'
fi
local mode=$(_num_to_hex 0 4)
local size=$(_num_to_hex 64 4)
echo -n -e "${mode}${raw}${size}" |
$KEYCTL_PROG padd logon $FSTYP:$keydesc @s >>$seqres.full
}
@@ -389,6 +404,44 @@ _user_do_enckey_status()
_user_do "$XFS_IO_PROG -c \"enckey_status $* $keyspec\" \"$mnt\""
}
# Require support for adding a key to a filesystem's fscrypt keyring via an
# "fscrypt-provisioning" keyring key.
_require_add_enckey_by_key_id()
{
local mnt=$1
# Userspace support
_require_xfs_io_command "add_enckey" "-k"
# Kernel support
if $XFS_IO_PROG -c "add_enckey -k 12345" "$mnt" \
|& grep -q 'Invalid argument'; then
_notrun "Kernel doesn't support key_id parameter to FS_IOC_ADD_ENCRYPTION_KEY"
fi
}
# Add a key of type "fscrypt-provisioning" to the session keyring and print the
# resulting key ID.
_add_fscrypt_provisioning_key()
{
local desc=$1
local type=$2
local raw=$3
# The format of the key payload must be:
#
# struct fscrypt_provisioning_key_payload {
# __u32 type;
# __u32 __reserved;
# __u8 raw[];
# };
#
local type_hex=$(_num_to_hex $type 4)
local reserved=$(_num_to_hex 0 4)
echo -n -e "${type_hex}${reserved}${raw}" |
$KEYCTL_PROG padd fscrypt-provisioning "$desc" @s
}
# Retrieve the encryption nonce of the given inode as a hex string. The nonce
# was randomly generated by the filesystem and isn't exposed directly to
# userspace. But it can be read using the filesystem's debugging tools.
@@ -717,6 +770,9 @@ FSCRYPT_MODE_ADIANTUM=9
FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR=1
FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER=2
_fscrypt_mode_name_to_num()
{
local name=$1
+155
View File
@@ -0,0 +1,155 @@
#! /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.
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
_cleanup()
{
cd /
rm -f $tmp.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
. ./common/encrypt
# remove previous $seqres.full before test
rm -f $seqres.full
# real QA test starts here
_supported_fs generic
_supported_os Linux
_require_scratch_encryption -v 2
_require_command "$KEYCTL_PROG" keyctl
_new_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 @s
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 @s
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 @s
echo -e "\n# Only keys of type fscrypt-provisioning can be added"
keyid=$(head -c 64 /dev/urandom | $KEYCTL_PROG padd logon foo:desc @s)
$XFS_IO_PROG -c "add_enckey -k $keyid" $SCRATCH_MNT
$KEYCTL_PROG unlink $keyid @s
# success, all done
status=0
exit
+73
View File
@@ -0,0 +1,73 @@
QA output created by 593
# ==========================
# Test with policy version 1
# ==========================
# Adding key to filesystem
Added encryption key with descriptor 0000111122223333
# Creating encrypted file
# Removing key from filesystem
Removed encryption key with descriptor 0000111122223333
cat: SCRATCH_MNT/dir/file: No such file or directory
# Adding fscrypt-provisioning key
# Adding key to filesystem via fscrypt-provisioning key
Added encryption key with descriptor 0000111122223333
# Reading encrypted file
contents
# Cleaning up
# ==========================
# Test with policy version 2
# ==========================
# Adding key to filesystem
Added encryption key with identifier 69b2f6edeee720cce0577937eb8a6751
# Creating encrypted file
# Removing key from filesystem
Removed encryption key with identifier 69b2f6edeee720cce0577937eb8a6751
cat: SCRATCH_MNT/dir/file: No such file or directory
# Adding fscrypt-provisioning key
# Adding key to filesystem via fscrypt-provisioning key
Added encryption key with identifier 69b2f6edeee720cce0577937eb8a6751
# Reading encrypted file
contents
# Cleaning up
# ================
# Validation tests
# ================
# Adding an invalid fscrypt-provisioning key fails
# ... bad type
add_key: Invalid argument
# ... bad type
add_key: Invalid argument
# ... raw key too small
add_key: Invalid argument
# ... raw key too large
add_key: Invalid argument
# keyctl_read() doesn't work on fscrypt-provisioning keys
keyctl_read_alloc: Operation not supported
# Only keys with the correct fscrypt_provisioning_key_payload::type field can be added
# ... keyring key is v1, filesystem wants v2 key
Error adding encryption key: Key was rejected by service
# ... keyring key is v2, filesystem wants v1 key
Error adding encryption key: Key was rejected by service
# Only keys of type fscrypt-provisioning can be added
Error adding encryption key: Key was rejected by service
+1
View File
@@ -595,3 +595,4 @@
590 auto prealloc preallocrw
591 auto quick rw pipe splice
592 auto quick encrypt
593 auto quick encrypt