From b09ba3318ebb2373df2ee516ee99aae4da24a8a3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 31 Oct 2020 00:23:44 -0700 Subject: [PATCH] generic: test that encryption nonces are unique and random Test that encryption nonces are unique and random, where randomness is approximated as "incompressible by the xz program". This gets indirectly tested by generic/399, but there are some gaps. It's good to test for this directly too. This test runs and passes on ext4 and f2fs. It doesn't currently run on ubifs because _get_encryption_nonce() isn't implemented for ubifs yet. (At some point I'll probably switch _get_encryption_nonce() to use FS_IOC_GET_ENCRYPTION_NONCE, which was added in Linux 5.7. But for now I'd like to keep the tests using it runnable on older kernels too.) Signed-off-by: Eric Biggers Signed-off-by: Eryu Guan --- tests/generic/613 | 118 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/613.out | 16 ++++++ tests/generic/group | 1 + 3 files changed, 135 insertions(+) create mode 100755 tests/generic/613 create mode 100644 tests/generic/613.out diff --git a/tests/generic/613 b/tests/generic/613 new file mode 100755 index 00000000..888ecf0a --- /dev/null +++ b/tests/generic/613 @@ -0,0 +1,118 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2020 Google LLC +# +# FS QA Test No. 613 +# +# Test that encryption nonces are unique and random, where randomness is +# approximated as "incompressible by the xz program". +# +# An encryption nonce is the 16-byte value that the filesystem generates for +# each encrypted file. These nonces must be unique in order to cause different +# files to be encrypted differently, which is an important security property. +# In practice, they need to be random to achieve that; and it's easy enough to +# test for both uniqueness and randomness, so we test for both. +# +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 +_require_scratch_encryption -v 2 +_require_get_encryption_nonce_support +_require_command "$XZ_PROG" xz + +_scratch_mkfs_encrypted &>> $seqres.full +_scratch_mount + +echo -e "\n# Adding encryption keys" +_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" +_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" -d $TEST_KEY_DESCRIPTOR + +# Create a bunch of encrypted files and directories -- enough for the uniqueness +# and randomness tests to be meaningful, but not so many that this test takes a +# long time. Test using both v1 and v2 encryption policies, and for each of +# those test the case of an encryption policy that is assigned to an empty +# directory as well as the case of a file created in an encrypted directory. +echo -e "\n# Creating encrypted files and directories" +inodes=() +for i in {1..50}; do + dir=$SCRATCH_MNT/v1_policy_dir_$i + mkdir $dir + inodes+=("$(stat -c %i $dir)") + _set_encpolicy $dir $TEST_KEY_DESCRIPTOR + + dir=$SCRATCH_MNT/v2_policy_dir_$i + mkdir $dir + inodes+=("$(stat -c %i $dir)") + _set_encpolicy $dir $TEST_KEY_IDENTIFIER +done +for i in {1..50}; do + file=$SCRATCH_MNT/v1_policy_dir_1/$i + touch $file + inodes+=("$(stat -c %i $file)") + + file=$SCRATCH_MNT/v2_policy_dir_1/$i + touch $file + inodes+=("$(stat -c %i $file)") +done +_scratch_unmount + +# Build files that contain all the nonces. nonces_hex contains them in hex, one +# per line. nonces_bin contains them in binary, all concatenated. +echo -e "\n# Getting encryption nonces from inodes" +echo -n > $tmp.nonces_hex +echo -n > $tmp.nonces_bin +for inode in "${inodes[@]}"; do + nonce=$(_get_encryption_nonce $SCRATCH_DEV $inode) + if (( ${#nonce} != 32 )) || [ -n "$(echo "$nonce" | tr -d 0-9a-fA-F)" ] + then + _fail "Expected nonce to be 16 bytes (32 hex characters), but got \"$nonce\"" + fi + echo $nonce >> $tmp.nonces_hex + echo -ne "$(echo $nonce | sed 's/[0-9a-fA-F]\{2\}/\\x\0/g')" \ + >> $tmp.nonces_bin +done + +# Verify the uniqueness and randomness of the nonces. In theory randomness +# implies uniqueness here, but it's easy enough to explicitly test for both. + +echo -e "\n# Verifying uniqueness of nonces" +echo "Listing non-unique nonces:" +sort < $tmp.nonces_hex | uniq -d + +echo -e "\n# Verifying randomness of nonces" +uncompressed_size=$(stat -c %s $tmp.nonces_bin) +echo "Uncompressed size is $uncompressed_size bytes" +compressed_size=$($XZ_PROG -c < $tmp.nonces_bin | wc -c) +echo "Compressed size is $compressed_size bytes" >> $seqres.full +# The xz format has 60 bytes of overhead. Go a bit lower to avoid flakiness. +if (( compressed_size >= uncompressed_size + 55 )); then + echo "Nonces are incompressible, as expected" +else + _fail "Nonces are compressible (non-random); compressed $uncompressed_size => $compressed_size bytes!" +fi + +# success, all done +status=0 +exit diff --git a/tests/generic/613.out b/tests/generic/613.out new file mode 100644 index 00000000..203a64f2 --- /dev/null +++ b/tests/generic/613.out @@ -0,0 +1,16 @@ +QA output created by 613 + +# Adding encryption keys +Added encryption key with identifier 69b2f6edeee720cce0577937eb8a6751 +Added encryption key with descriptor 0000111122223333 + +# Creating encrypted files and directories + +# Getting encryption nonces from inodes + +# Verifying uniqueness of nonces +Listing non-unique nonces: + +# Verifying randomness of nonces +Uncompressed size is 3200 bytes +Nonces are incompressible, as expected diff --git a/tests/generic/group b/tests/generic/group index 8054d874..31057ac8 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -615,3 +615,4 @@ 610 auto quick prealloc zero 611 auto quick attr 612 auto quick clone +613 auto quick encrypt -- 2.30.2