linux-fscrypt.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies
@ 2019-11-11 21:04 Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 1/5] fscrypt-crypt-util: create key_and_iv_params structure Eric Biggers
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

Hello,

This series adds an xfstest which tests that the encryption for
IV_INO_LBLK_64 encryption policies is being done correctly.

IV_INO_LBLK_64 is a new fscrypt policy flag which modifies the
encryption to be optimized for inline encryption hardware compliant with
the UFS v2.1 standard or the upcoming version of the eMMC standard.  For
more information, see the kernel patchset:
https://lore.kernel.org/linux-fscrypt/20191024215438.138489-1-ebiggers@kernel.org/T/#u

This is RFC for now since the kernel patches aren't in mainline yet
(they're queued for 5.5).

To run on ext4 this test also needs a version of e2fsprogs built from
the master branch, for support for the stable_inodes filesystem feature.

Eric Biggers (5):
  fscrypt-crypt-util: create key_and_iv_params structure
  fscrypt-crypt-util: add HKDF context constants
  common/encrypt: create named variables for UAPI constants
  common/encrypt: support verifying ciphertext of IV_INO_LBLK_64
    policies
  generic: verify ciphertext of IV_INO_LBLK_64 encryption policies

 common/encrypt           | 126 +++++++++++++++++++++++++-------
 src/fscrypt-crypt-util.c | 151 ++++++++++++++++++++++++++++-----------
 tests/generic/805        |  43 +++++++++++
 tests/generic/805.out    |   6 ++
 tests/generic/group      |   1 +
 5 files changed, 259 insertions(+), 68 deletions(-)
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out

-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH 1/5] fscrypt-crypt-util: create key_and_iv_params structure
  2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
@ 2019-11-11 21:04 ` Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 2/5] fscrypt-crypt-util: add HKDF context constants Eric Biggers
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

From: Eric Biggers <ebiggers@google.com>

In preparation for adding 3 more input parameters to get_key_and_iv(),
create a structure to hold the input parameters so that the code doesn't
get too unwieldy.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 src/fscrypt-crypt-util.c | 82 +++++++++++++++++++++-------------------
 1 file changed, 44 insertions(+), 38 deletions(-)

diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index f5fd8386..bafc15e0 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -1694,66 +1694,75 @@ static u8 parse_mode_number(const char *arg)
 	return num;
 }
 
+struct key_and_iv_params {
+	u8 master_key[MAX_KEY_SIZE];
+	int master_key_size;
+	enum kdf_algorithm kdf;
+	u8 mode_num;
+	u8 file_nonce[FILE_NONCE_SIZE];
+	bool file_nonce_specified;
+};
+
 /*
  * Get the key and starting IV with which the encryption will actually be done.
  * If a KDF was specified, a subkey is derived from the master key and the mode
  * number or file nonce.  Otherwise, the master key is used directly.
  */
-static void get_key_and_iv(const u8 *master_key, size_t master_key_size,
-			   enum kdf_algorithm kdf,
-			   u8 mode_num, const u8 nonce[FILE_NONCE_SIZE],
+static void get_key_and_iv(const struct key_and_iv_params *params,
 			   u8 *real_key, size_t real_key_size,
 			   struct fscrypt_iv *iv)
 {
-	bool nonce_in_iv = false;
+	bool file_nonce_in_iv = false;
 	struct aes_key aes_key;
 	u8 info[8 + 1 + FILE_NONCE_SIZE] = "fscrypt";
 	size_t infolen = 8;
 	size_t i;
 
-	ASSERT(real_key_size <= master_key_size);
+	ASSERT(real_key_size <= params->master_key_size);
 
 	memset(iv, 0, sizeof(*iv));
 
-	switch (kdf) {
+	switch (params->kdf) {
 	case KDF_NONE:
-		if (mode_num != 0)
+		if (params->mode_num != 0)
 			die("--mode-num isn't supported with --kdf=none");
-		memcpy(real_key, master_key, real_key_size);
-		nonce_in_iv = true;
+		memcpy(real_key, params->master_key, real_key_size);
+		file_nonce_in_iv = true;
 		break;
 	case KDF_AES_128_ECB:
-		if (nonce == NULL)
+		if (!params->file_nonce_specified)
 			die("--file-nonce is required with --kdf=AES-128-ECB");
-		if (mode_num != 0)
+		if (params->mode_num != 0)
 			die("--mode-num isn't supported with --kdf=AES-128-ECB");
 		STATIC_ASSERT(FILE_NONCE_SIZE == AES_128_KEY_SIZE);
 		ASSERT(real_key_size % AES_BLOCK_SIZE == 0);
-		aes_setkey(&aes_key, nonce, AES_128_KEY_SIZE);
+		aes_setkey(&aes_key, params->file_nonce, AES_128_KEY_SIZE);
 		for (i = 0; i < real_key_size; i += AES_BLOCK_SIZE)
-			aes_encrypt(&aes_key, &master_key[i], &real_key[i]);
+			aes_encrypt(&aes_key, &params->master_key[i],
+				    &real_key[i]);
 		break;
 	case KDF_HKDF_SHA512:
-		if (mode_num != 0) {
+		if (params->mode_num != 0) {
 			info[infolen++] = 3; /* HKDF_CONTEXT_PER_MODE_KEY */
-			info[infolen++] = mode_num;
-			nonce_in_iv = true;
-		} else if (nonce != NULL) {
+			info[infolen++] = params->mode_num;
+			file_nonce_in_iv = true;
+		} else if (params->file_nonce_specified) {
 			info[infolen++] = 2; /* HKDF_CONTEXT_PER_FILE_KEY */
-			memcpy(&info[infolen], nonce, FILE_NONCE_SIZE);
+			memcpy(&info[infolen], params->file_nonce,
+			       FILE_NONCE_SIZE);
 			infolen += FILE_NONCE_SIZE;
 		} else {
 			die("With --kdf=HKDF-SHA512, at least one of --file-nonce and --mode-num must be specified");
 		}
-		hkdf_sha512(master_key, master_key_size, NULL, 0,
-			    info, infolen, real_key, real_key_size);
+		hkdf_sha512(params->master_key, params->master_key_size,
+			    NULL, 0, info, infolen, real_key, real_key_size);
 		break;
 	default:
 		ASSERT(0);
 	}
 
-	if (nonce_in_iv && nonce != NULL)
-		memcpy(&iv->bytes[8], nonce, FILE_NONCE_SIZE);
+	if (file_nonce_in_iv && params->file_nonce_specified)
+		memcpy(&iv->bytes[8], params->file_nonce, FILE_NONCE_SIZE);
 }
 
 enum {
@@ -1781,19 +1790,16 @@ int main(int argc, char *argv[])
 {
 	size_t block_size = 4096;
 	bool decrypting = false;
-	u8 _file_nonce[FILE_NONCE_SIZE];
-	u8 *file_nonce = NULL;
-	enum kdf_algorithm kdf = KDF_NONE;
-	u8 mode_num = 0;
+	struct key_and_iv_params params;
 	size_t padding = 0;
 	const struct fscrypt_cipher *cipher;
-	u8 master_key[MAX_KEY_SIZE];
-	int master_key_size;
 	u8 real_key[MAX_KEY_SIZE];
 	struct fscrypt_iv iv;
 	char *tmp;
 	int c;
 
+	memset(&params, 0, sizeof(params));
+
 	aes_init();
 
 #ifdef ENABLE_ALG_TESTS
@@ -1816,19 +1822,19 @@ int main(int argc, char *argv[])
 			decrypting = true;
 			break;
 		case OPT_FILE_NONCE:
-			if (hex2bin(optarg, _file_nonce, FILE_NONCE_SIZE) !=
-			    FILE_NONCE_SIZE)
+			if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE)
+			    != FILE_NONCE_SIZE)
 				die("Invalid file nonce: %s", optarg);
-			file_nonce = _file_nonce;
+			params.file_nonce_specified = true;
 			break;
 		case OPT_HELP:
 			usage(stdout);
 			return 0;
 		case OPT_KDF:
-			kdf = parse_kdf_algorithm(optarg);
+			params.kdf = parse_kdf_algorithm(optarg);
 			break;
 		case OPT_MODE_NUM:
-			mode_num = parse_mode_number(optarg);
+			params.mode_num = parse_mode_number(optarg);
 			break;
 		case OPT_PADDING:
 			padding = strtoul(optarg, &tmp, 10);
@@ -1857,14 +1863,14 @@ int main(int argc, char *argv[])
 		die("Block size of %zu bytes is too small for cipher %s",
 		    block_size, cipher->name);
 
-	master_key_size = hex2bin(argv[1], master_key, MAX_KEY_SIZE);
-	if (master_key_size < 0)
+	params.master_key_size = hex2bin(argv[1], params.master_key,
+					 MAX_KEY_SIZE);
+	if (params.master_key_size < 0)
 		die("Invalid master_key: %s", argv[1]);
-	if (master_key_size < cipher->keysize)
+	if (params.master_key_size < cipher->keysize)
 		die("Master key is too short for cipher %s", cipher->name);
 
-	get_key_and_iv(master_key, master_key_size, kdf, mode_num, file_nonce,
-		       real_key, cipher->keysize, &iv);
+	get_key_and_iv(&params, real_key, cipher->keysize, &iv);
 
 	crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding);
 	return 0;
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 2/5] fscrypt-crypt-util: add HKDF context constants
  2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 1/5] fscrypt-crypt-util: create key_and_iv_params structure Eric Biggers
@ 2019-11-11 21:04 ` Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 3/5] common/encrypt: create named variables for UAPI constants Eric Biggers
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

From: Eric Biggers <ebiggers@google.com>

Use #defines rather than hard-coded numbers + comments.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 src/fscrypt-crypt-util.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index bafc15e0..30f5e585 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -1703,6 +1703,10 @@ struct key_and_iv_params {
 	bool file_nonce_specified;
 };
 
+#define HKDF_CONTEXT_KEY_IDENTIFIER	1
+#define HKDF_CONTEXT_PER_FILE_KEY	2
+#define HKDF_CONTEXT_PER_MODE_KEY	3
+
 /*
  * Get the key and starting IV with which the encryption will actually be done.
  * If a KDF was specified, a subkey is derived from the master key and the mode
@@ -1743,11 +1747,11 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 		break;
 	case KDF_HKDF_SHA512:
 		if (params->mode_num != 0) {
-			info[infolen++] = 3; /* HKDF_CONTEXT_PER_MODE_KEY */
+			info[infolen++] = HKDF_CONTEXT_PER_MODE_KEY;
 			info[infolen++] = params->mode_num;
 			file_nonce_in_iv = true;
 		} else if (params->file_nonce_specified) {
-			info[infolen++] = 2; /* HKDF_CONTEXT_PER_FILE_KEY */
+			info[infolen++] = HKDF_CONTEXT_PER_FILE_KEY;
 			memcpy(&info[infolen], params->file_nonce,
 			       FILE_NONCE_SIZE);
 			infolen += FILE_NONCE_SIZE;
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 3/5] common/encrypt: create named variables for UAPI constants
  2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 1/5] fscrypt-crypt-util: create key_and_iv_params structure Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 2/5] fscrypt-crypt-util: add HKDF context constants Eric Biggers
@ 2019-11-11 21:04 ` Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 4/5] common/encrypt: support verifying ciphertext of IV_INO_LBLK_64 policies Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 5/5] generic: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

From: Eric Biggers <ebiggers@google.com>

Use named variables rather than hard-coded numbers + comments.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/encrypt | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/common/encrypt b/common/encrypt
index 90f931fc..b967c65a 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -664,16 +664,26 @@ _do_verify_ciphertext_for_encryption_policy()
 	done
 }
 
+# fscrypt UAPI constants (see <linux/fscrypt.h>)
+
+FSCRYPT_MODE_AES_256_XTS=1
+FSCRYPT_MODE_AES_256_CTS=4
+FSCRYPT_MODE_AES_128_CBC=5
+FSCRYPT_MODE_AES_128_CTS=6
+FSCRYPT_MODE_ADIANTUM=9
+
+FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
+
 _fscrypt_mode_name_to_num()
 {
 	local name=$1
 
 	case "$name" in
-	AES-256-XTS)		echo 1 ;; # FS_ENCRYPTION_MODE_AES_256_XTS
-	AES-256-CTS-CBC)	echo 4 ;; # FS_ENCRYPTION_MODE_AES_256_CTS
-	AES-128-CBC-ESSIV)	echo 5 ;; # FS_ENCRYPTION_MODE_AES_128_CBC
-	AES-128-CTS-CBC)	echo 6 ;; # FS_ENCRYPTION_MODE_AES_128_CTS
-	Adiantum)		echo 9 ;; # FS_ENCRYPTION_MODE_ADIANTUM
+	AES-256-XTS)		echo $FSCRYPT_MODE_AES_256_XTS ;;
+	AES-256-CTS-CBC)	echo $FSCRYPT_MODE_AES_256_CTS ;;
+	AES-128-CBC-ESSIV)	echo $FSCRYPT_MODE_AES_128_CBC ;;
+	AES-128-CTS-CBC)	echo $FSCRYPT_MODE_AES_128_CTS ;;
+	Adiantum)		echo $FSCRYPT_MODE_ADIANTUM ;;
 	*)			_fail "Unknown fscrypt mode: $name" ;;
 	esac
 }
@@ -705,7 +715,7 @@ _verify_ciphertext_for_encryption_policy()
 			     $filenames_encryption_mode ]; then
 				_fail "For direct key mode, contents and filenames modes must match"
 			fi
-			(( policy_flags |= 0x04 )) # FS_POLICY_FLAG_DIRECT_KEY
+			(( policy_flags |= FSCRYPT_POLICY_FLAG_DIRECT_KEY ))
 			;;
 		*)
 			_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
@@ -721,11 +731,11 @@ _verify_ciphertext_for_encryption_policy()
 	if (( policy_version > 1 )); then
 		set_encpolicy_args+=" -v 2"
 		crypt_util_args+=" --kdf=HKDF-SHA512"
-		if (( policy_flags & 0x04 )); then
+		if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
 			crypt_util_args+=" --mode-num=$contents_mode_num"
 		fi
 	else
-		if (( policy_flags & 0x04 )); then
+		if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
 			crypt_util_args+=" --kdf=none"
 		else
 			crypt_util_args+=" --kdf=AES-128-ECB"
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 4/5] common/encrypt: support verifying ciphertext of IV_INO_LBLK_64 policies
  2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
                   ` (2 preceding siblings ...)
  2019-11-11 21:04 ` [RFC PATCH 3/5] common/encrypt: create named variables for UAPI constants Eric Biggers
@ 2019-11-11 21:04 ` Eric Biggers
  2019-11-11 21:04 ` [RFC PATCH 5/5] generic: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

From: Eric Biggers <ebiggers@google.com>

Update _verify_ciphertext_for_encryption_policy() to support encryption
policies with the IV_INO_LBLK_64 flag set.

This flag modifies the encryption to include the inode number in the IVs
and to use a key derived from the tuple [master_key, fs_uuid, mode_num].
Since the file nonce is *not* included in this key derivation, multiple
files can use the same key.

This flag is supported by v2 encryption policies only -- not by v1.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/encrypt           | 100 +++++++++++++++++++++++++++++++--------
 src/fscrypt-crypt-util.c |  67 ++++++++++++++++++++++++--
 2 files changed, 144 insertions(+), 23 deletions(-)

diff --git a/common/encrypt b/common/encrypt
index b967c65a..2e9908ad 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -6,7 +6,7 @@
 
 #
 # _require_scratch_encryption [-c CONTENTS_MODE] [-n FILENAMES_MODE]
-#			      [-v POLICY_VERSION]
+#			      [-f POLICY_FLAGS] [-v POLICY_VERSION]
 #
 # Require encryption support on the scratch device.
 #
@@ -69,15 +69,20 @@ _require_encryption_policy_support()
 	local mnt=$1
 	local dir=$mnt/tmpdir
 	local set_encpolicy_args=""
+	local policy_flags=0
 	local policy_version=1
 	local c
 
 	OPTIND=2
-	while getopts "c:n:v:" c; do
+	while getopts "c:n:f:v:" c; do
 		case $c in
 		c|n)
 			set_encpolicy_args+=" -$c $OPTARG"
 			;;
+		f)
+			set_encpolicy_args+=" -$c $OPTARG"
+			policy_flags=$OPTARG
+			;;
 		v)
 			set_encpolicy_args+=" -$c $OPTARG"
 			policy_version=$OPTARG
@@ -92,6 +97,12 @@ _require_encryption_policy_support()
 	echo "Checking whether kernel supports encryption policy: $set_encpolicy_args" \
 		>> $seqres.full
 
+	if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
+		_scratch_unmount
+		_scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
+		_scratch_mount
+	fi
+
 	mkdir $dir
 	if (( policy_version > 1 )); then
 		_require_xfs_io_command "get_encpolicy" "-t"
@@ -159,6 +170,23 @@ _scratch_mkfs_sized_encrypted()
 	esac
 }
 
+# Like _scratch_mkfs_encrypted(), but add -O stable_inodes if applicable for the
+# filesystem type.  This is necessary for using encryption policies that include
+# the inode number in the IVs, e.g. policies with the IV_INO_LBLK_64 flag set.
+_scratch_mkfs_stable_inodes_encrypted()
+{
+	case $FSTYP in
+	ext4)
+		if ! _scratch_mkfs -O encrypt -O stable_inodes; then
+			_notrun "-O stable_inodes is not supported"
+		fi
+		;;
+	*)
+		_scratch_mkfs_encrypted
+		;;
+	esac
+}
+
 # Give the invoking shell a new session keyring.  This makes any keys we add to
 # the session keyring scoped to the lifetime of the test script.
 _new_session_keyring()
@@ -568,7 +596,8 @@ _do_verify_ciphertext_for_encryption_policy()
 	local set_encpolicy_args=$4
 	local keyspec=$5
 	local raw_key_hex=$6
-	local crypt_cmd="$here/src/fscrypt-crypt-util $7"
+	local crypt_contents_cmd="$here/src/fscrypt-crypt-util $7"
+	local crypt_filename_cmd="$here/src/fscrypt-crypt-util $8"
 
 	local blocksize=$(_get_block_size $SCRATCH_MNT)
 	local test_contents_files=()
@@ -626,14 +655,15 @@ _do_verify_ciphertext_for_encryption_policy()
 		read -r src inode blocklist <<< "$f"
 		nonce=$(_get_encryption_nonce $SCRATCH_DEV $inode)
 		_dump_ciphertext_blocks $SCRATCH_DEV $blocklist > $tmp.actual_contents
-		$crypt_cmd $contents_encryption_mode $raw_key_hex \
+		$crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
 			--file-nonce=$nonce --block-size=$blocksize \
-			< $src > $tmp.expected_contents
+			--inode-number=$inode < $src > $tmp.expected_contents
 		if ! cmp $tmp.expected_contents $tmp.actual_contents; then
 			_fail "Expected encrypted contents != actual encrypted contents.  File: $f"
 		fi
-		$crypt_cmd $contents_encryption_mode $raw_key_hex --decrypt \
-			--file-nonce=$nonce --block-size=$blocksize \
+		$crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
+			--decrypt --file-nonce=$nonce --block-size=$blocksize \
+			--inode-number=$inode \
 			< $tmp.actual_contents > $tmp.decrypted_contents
 		if ! cmp $src $tmp.decrypted_contents; then
 			_fail "Contents decryption sanity check failed.  File: $f"
@@ -647,16 +677,17 @@ _do_verify_ciphertext_for_encryption_policy()
 		_get_ciphertext_filename $SCRATCH_DEV $inode $dir_inode \
 			> $tmp.actual_name
 		echo -n "$name" | \
-			$crypt_cmd $filenames_encryption_mode $raw_key_hex \
-			--file-nonce=$nonce --padding=$padding \
-			--block-size=255 > $tmp.expected_name
+			$crypt_filename_cmd $filenames_encryption_mode \
+			$raw_key_hex --file-nonce=$nonce --padding=$padding \
+			--block-size=255 --inode-number=$dir_inode \
+			> $tmp.expected_name
 		if ! cmp $tmp.expected_name $tmp.actual_name; then
 			_fail "Expected encrypted filename != actual encrypted filename.  File: $f"
 		fi
-		$crypt_cmd $filenames_encryption_mode $raw_key_hex --decrypt \
-			--file-nonce=$nonce --padding=$padding \
-			--block-size=255 < $tmp.actual_name \
-			> $tmp.decrypted_name
+		$crypt_filename_cmd $filenames_encryption_mode $raw_key_hex \
+			--decrypt --file-nonce=$nonce --padding=$padding \
+			--block-size=255 --inode-number=$dir_inode \
+			< $tmp.actual_name > $tmp.decrypted_name
 		decrypted_name=$(tr -d '\0' < $tmp.decrypted_name)
 		if [ "$name" != "$decrypted_name" ]; then
 			_fail "Filename decryption sanity check failed ($name != $decrypted_name).  File: $f"
@@ -673,6 +704,7 @@ FSCRYPT_MODE_AES_128_CTS=6
 FSCRYPT_MODE_ADIANTUM=9
 
 FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
+FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
 
 _fscrypt_mode_name_to_num()
 {
@@ -692,8 +724,13 @@ _fscrypt_mode_name_to_num()
 # policy of the specified type is used.
 #
 # The first two parameters are the contents and filenames encryption modes to
-# test.  Optionally, also specify 'direct' to test the DIRECT_KEY flag, and/or
-# 'v2' to test v2 policies.
+# test.  The following optional parameters are also accepted to further modify
+# the type of encryption policy that is tested:
+#
+#	'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
+#
 _verify_ciphertext_for_encryption_policy()
 {
 	local contents_encryption_mode=$1
@@ -703,6 +740,8 @@ _verify_ciphertext_for_encryption_policy()
 	local policy_flags=0
 	local set_encpolicy_args=""
 	local crypt_util_args=""
+	local crypt_util_contents_args=""
+	local crypt_util_filename_args=""
 
 	shift 2
 	for opt; do
@@ -717,6 +756,9 @@ _verify_ciphertext_for_encryption_policy()
 			fi
 			(( policy_flags |= FSCRYPT_POLICY_FLAG_DIRECT_KEY ))
 			;;
+		iv_ino_lblk_64)
+			(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 ))
+			;;
 		*)
 			_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
 			;;
@@ -732,9 +774,19 @@ _verify_ciphertext_for_encryption_policy()
 		set_encpolicy_args+=" -v 2"
 		crypt_util_args+=" --kdf=HKDF-SHA512"
 		if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
+			if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
+				_fail "'direct' and 'iv_ino_lblk_64' options are mutually exclusive"
+			fi
 			crypt_util_args+=" --mode-num=$contents_mode_num"
+		elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
+			crypt_util_args+=" --iv-ino-lblk-64"
+			crypt_util_contents_args+=" --mode-num=$contents_mode_num"
+			crypt_util_filename_args+=" --mode-num=$filenames_mode_num"
 		fi
 	else
+		if (( policy_flags & ~FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
+			_fail "unsupported flags for v1 policy: $policy_flags"
+		fi
 		if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
 			crypt_util_args+=" --kdf=none"
 		else
@@ -743,7 +795,7 @@ _verify_ciphertext_for_encryption_policy()
 	fi
 	set_encpolicy_args=${set_encpolicy_args# }
 
-	_require_scratch_encryption $set_encpolicy_args
+	_require_scratch_encryption $set_encpolicy_args -f $policy_flags
 	_require_test_program "fscrypt-crypt-util"
 	_require_xfs_io_command "fiemap"
 	_require_get_encryption_nonce_support
@@ -753,9 +805,18 @@ _verify_ciphertext_for_encryption_policy()
 	fi
 
 	echo "Creating encryption-capable filesystem" >> $seqres.full
-	_scratch_mkfs_encrypted &>> $seqres.full
+	if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
+		_scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
+	else
+		_scratch_mkfs_encrypted &>> $seqres.full
+	fi
 	_scratch_mount
 
+	crypt_util_args+=" --fs-uuid=$(blkid -s UUID -o value $SCRATCH_DEV | tr -d -)"
+
+	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
@@ -781,5 +842,6 @@ _verify_ciphertext_for_encryption_policy()
 		"$set_encpolicy_args" \
 		"$keyspec" \
 		"$raw_key_hex" \
-		"$crypt_util_args"
+		"$crypt_util_contents_args" \
+		"$crypt_util_filename_args"
 }
diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index 30f5e585..1bf8f95c 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -62,7 +62,15 @@ static void usage(FILE *fp)
 "                                Default: 4096 bytes\n"
 "  --decrypt                   Decrypt instead of encrypt\n"
 "  --file-nonce=NONCE          File's nonce as a 32-character hex string\n"
+"  --fs-uuid=UUID              The filesystem UUID as a 32-character hex string.\n"
+"                                Only required for --iv-ino-lblk-64.\n"
 "  --help                      Show this help\n"
+"  --inode-number=INUM         The file's inode number.  Only required for\n"
+"                                --iv-ino-lblk-64.\n"
+"  --iv-ino-lblk-64            Use the format where the IVs include the inode\n"
+"                                number and the same key is shared across files.\n"
+"                                Requires --kdf=HKDF-SHA512, --fs-uuid,\n"
+"                                --inode-number, and --mode-num.\n"
 "  --kdf=KDF                   Key derivation function to use: AES-128-ECB,\n"
 "                                HKDF-SHA512, or none.  Default: none\n"
 "  --mode-num=NUM              Derive per-mode key using mode number NUM\n"
@@ -1576,6 +1584,7 @@ static void test_adiantum(void)
  *----------------------------------------------------------------------------*/
 
 #define FILE_NONCE_SIZE		16
+#define UUID_SIZE		16
 #define MAX_KEY_SIZE		64
 
 static const struct fscrypt_cipher {
@@ -1694,6 +1703,18 @@ static u8 parse_mode_number(const char *arg)
 	return num;
 }
 
+static u32 parse_inode_number(const char *arg)
+{
+	char *tmp;
+	unsigned long long num = strtoull(arg, &tmp, 10);
+
+	if (num <= 0 || *tmp)
+		die("Invalid inode number: %s", arg);
+	if ((u32)num != num)
+		die("Inode number %s is too large; must be 32-bit", arg);
+	return num;
+}
+
 struct key_and_iv_params {
 	u8 master_key[MAX_KEY_SIZE];
 	int master_key_size;
@@ -1701,11 +1722,16 @@ struct key_and_iv_params {
 	u8 mode_num;
 	u8 file_nonce[FILE_NONCE_SIZE];
 	bool file_nonce_specified;
+	bool iv_ino_lblk_64;
+	u32 inode_number;
+	u8 fs_uuid[UUID_SIZE];
+	bool fs_uuid_specified;
 };
 
 #define HKDF_CONTEXT_KEY_IDENTIFIER	1
 #define HKDF_CONTEXT_PER_FILE_KEY	2
-#define HKDF_CONTEXT_PER_MODE_KEY	3
+#define HKDF_CONTEXT_DIRECT_KEY		3
+#define HKDF_CONTEXT_IV_INO_LBLK_64_KEY	4
 
 /*
  * Get the key and starting IV with which the encryption will actually be done.
@@ -1718,7 +1744,7 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 {
 	bool file_nonce_in_iv = false;
 	struct aes_key aes_key;
-	u8 info[8 + 1 + FILE_NONCE_SIZE] = "fscrypt";
+	u8 info[8 + 1 + 1 + UUID_SIZE] = "fscrypt";
 	size_t infolen = 8;
 	size_t i;
 
@@ -1726,6 +1752,9 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 
 	memset(iv, 0, sizeof(*iv));
 
+	if (params->iv_ino_lblk_64 && params->kdf != KDF_HKDF_SHA512)
+		die("--iv-ino-lblk-64 requires --kdf=HKDF-SHA512");
+
 	switch (params->kdf) {
 	case KDF_NONE:
 		if (params->mode_num != 0)
@@ -1746,8 +1775,20 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 				    &real_key[i]);
 		break;
 	case KDF_HKDF_SHA512:
-		if (params->mode_num != 0) {
-			info[infolen++] = HKDF_CONTEXT_PER_MODE_KEY;
+		if (params->iv_ino_lblk_64) {
+			if (!params->fs_uuid_specified)
+				die("--iv-ino-lblk-64 requires --fs-uuid");
+			if (params->inode_number == 0)
+				die("--iv-ino-lblk-64 requires --inode-number");
+			if (params->mode_num == 0)
+				die("--iv-ino-lblk-64 requires --mode-num");
+			info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_64_KEY;
+			info[infolen++] = params->mode_num;
+			memcpy(&info[infolen], params->fs_uuid, UUID_SIZE);
+			infolen += UUID_SIZE;
+			put_unaligned_le32(params->inode_number, &iv->bytes[4]);
+		} else if (params->mode_num != 0) {
+			info[infolen++] = HKDF_CONTEXT_DIRECT_KEY;
 			info[infolen++] = params->mode_num;
 			file_nonce_in_iv = true;
 		} else if (params->file_nonce_specified) {
@@ -1773,7 +1814,10 @@ enum {
 	OPT_BLOCK_SIZE,
 	OPT_DECRYPT,
 	OPT_FILE_NONCE,
+	OPT_FS_UUID,
 	OPT_HELP,
+	OPT_INODE_NUMBER,
+	OPT_IV_INO_LBLK_64,
 	OPT_KDF,
 	OPT_MODE_NUM,
 	OPT_PADDING,
@@ -1783,7 +1827,10 @@ static const struct option longopts[] = {
 	{ "block-size",      required_argument, NULL, OPT_BLOCK_SIZE },
 	{ "decrypt",         no_argument,       NULL, OPT_DECRYPT },
 	{ "file-nonce",      required_argument, NULL, OPT_FILE_NONCE },
+	{ "fs-uuid",         required_argument, NULL, OPT_FS_UUID },
 	{ "help",            no_argument,       NULL, OPT_HELP },
+	{ "inode-number",    required_argument, NULL, OPT_INODE_NUMBER },
+	{ "iv-ino-lblk-64",  no_argument,       NULL, OPT_IV_INO_LBLK_64 },
 	{ "kdf",             required_argument, NULL, OPT_KDF },
 	{ "mode-num",        required_argument, NULL, OPT_MODE_NUM },
 	{ "padding",         required_argument, NULL, OPT_PADDING },
@@ -1831,9 +1878,21 @@ int main(int argc, char *argv[])
 				die("Invalid file nonce: %s", optarg);
 			params.file_nonce_specified = true;
 			break;
+		case OPT_FS_UUID:
+			if (hex2bin(optarg, params.fs_uuid, UUID_SIZE)
+			    != UUID_SIZE)
+				die("Invalid filesystem UUID: %s", optarg);
+			params.fs_uuid_specified = true;
+			break;
 		case OPT_HELP:
 			usage(stdout);
 			return 0;
+		case OPT_INODE_NUMBER:
+			params.inode_number = parse_inode_number(optarg);
+			break;
+		case OPT_IV_INO_LBLK_64:
+			params.iv_ino_lblk_64 = true;
+			break;
 		case OPT_KDF:
 			params.kdf = parse_kdf_algorithm(optarg);
 			break;
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 5/5] generic: verify ciphertext of IV_INO_LBLK_64 encryption policies
  2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
                   ` (3 preceding siblings ...)
  2019-11-11 21:04 ` [RFC PATCH 4/5] common/encrypt: support verifying ciphertext of IV_INO_LBLK_64 policies Eric Biggers
@ 2019-11-11 21:04 ` Eric Biggers
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Biggers @ 2019-11-11 21:04 UTC (permalink / raw)
  To: fstests; +Cc: linux-fscrypt, Satya Tangirala

From: Eric Biggers <ebiggers@google.com>

Verify ciphertext for v2 encryption policies that use the IV_INO_LBLK_64
flag and use AES-256-XTS to encrypt file contents and AES-256-CTS-CBC to
encrypt file names.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/805     | 43 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/805.out |  6 ++++++
 tests/generic/group   |  1 +
 3 files changed, 50 insertions(+)
 create mode 100644 tests/generic/805
 create mode 100644 tests/generic/805.out

diff --git a/tests/generic/805 b/tests/generic/805
new file mode 100644
index 00000000..d07b620b
--- /dev/null
+++ b/tests/generic/805
@@ -0,0 +1,43 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2019 Google LLC
+#
+# FS QA Test generic/805
+#
+# Verify ciphertext for v2 encryption policies that use the IV_INO_LBLK_64 flag
+# and use AES-256-XTS to encrypt file contents and AES-256-CTS-CBC to encrypt
+# file names.
+#
+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
+
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC \
+	v2 iv_ino_lblk_64
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/805.out b/tests/generic/805.out
new file mode 100644
index 00000000..84ec773f
--- /dev/null
+++ b/tests/generic/805.out
@@ -0,0 +1,6 @@
+QA output created by 805
+
+Verifying ciphertext with parameters:
+	contents_encryption_mode: AES-256-XTS
+	filenames_encryption_mode: AES-256-CTS-CBC
+	options: v2 iv_ino_lblk_64
diff --git a/tests/generic/group b/tests/generic/group
index e5d0c1da..02e4cc45 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -588,3 +588,4 @@
 583 auto quick encrypt
 584 auto quick encrypt
 585 auto rename
+805 auto quick encrypt
-- 
2.24.0.rc1.363.gb1bccd3e3d-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-11-11 21:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-11 21:04 [RFC PATCH 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
2019-11-11 21:04 ` [RFC PATCH 1/5] fscrypt-crypt-util: create key_and_iv_params structure Eric Biggers
2019-11-11 21:04 ` [RFC PATCH 2/5] fscrypt-crypt-util: add HKDF context constants Eric Biggers
2019-11-11 21:04 ` [RFC PATCH 3/5] common/encrypt: create named variables for UAPI constants Eric Biggers
2019-11-11 21:04 ` [RFC PATCH 4/5] common/encrypt: support verifying ciphertext of IV_INO_LBLK_64 policies Eric Biggers
2019-11-11 21:04 ` [RFC PATCH 5/5] generic: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).