All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support
@ 2022-02-28  7:47 Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option Eric Biggers
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

This series adds xfstests for the "hardware-wrapped inline encryption
keys" feature which I've proposed adding to the kernel
(https://lore.kernel.org/linux-fscrypt/20220228070520.74082-1-ebiggers@kernel.org/T/#u).

This applies to the master branch of xfstests (commit 2ea74ba4e70b).

For now, the new tests just include ciphertext verification tests.
These are the most important type of test to have here, as they validate
the on-disk format, which must be gotten right from the start.  They
verify that all the cryptography is implemented correctly, including
both the parts handled by the hardware and the parts handled by the
kernel.  Naturally, to do their work they exercise the new UAPIs too.

For now this is an RFC, as the corresponding kernel patches have yet to
be applied.  Patches 1-5 are cleanups that could be applied earlier, but
I need to look them over again first and probably will resend them.

In any case, any reviews would be greatly appreciated!

I've verified that the new tests run and pass when all their
prerequisites are met, namely:                                                             
                                                                         
- Hardware supporting the feature must be present.  I tested this on the
  SM8350 HDK (note: this currently requires a custom TrustZone image);
  this hardware is compatible with both of IV_INO_LBLK_{64,32}.
- The kernel patches for hardware-wrapped key support must be applied.
- The filesystem must be ext4 or f2fs.
- The kernel must have CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y.
- The fscryptctl program must be available, and must have my patches for
  hardware-wrapped key support applied.  These can currently be found at
  https://github.com/ebiggers/fscryptctl/tree/wip-wrapped-keys.

Eric Biggers (8):
  fscrypt-crypt-util: use an explicit --direct-key option
  fscrypt-crypt-util: refactor get_key_and_iv()
  fscrypt-crypt-util: add support for dumping key identifier
  common/encrypt: log full ciphertext verification params
  common/encrypt: verify the key identifiers
  fscrypt-crypt-util: add hardware KDF support
  common/encrypt: support hardware-wrapped key testing
  generic: verify ciphertext with hardware-wrapped keys

 common/config            |   1 +
 common/encrypt           | 149 +++++++++++--
 src/fscrypt-crypt-util.c | 454 ++++++++++++++++++++++++++++++++-------
 tests/generic/900        |  30 +++
 tests/generic/900.out    |   6 +
 tests/generic/901        |  30 +++
 tests/generic/901.out    |   6 +
 7 files changed, 579 insertions(+), 97 deletions(-)
 create mode 100755 tests/generic/900
 create mode 100644 tests/generic/900.out
 create mode 100755 tests/generic/901
 create mode 100644 tests/generic/901.out


base-commit: 2ea74ba4e70b546279896e2a733c8c7f4b206193
-- 
2.35.1


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

* [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 2/8] fscrypt-crypt-util: refactor get_key_and_iv() Eric Biggers
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

Make fscrypt-crypt-util use an option --direct-key to specify the use of
the DIRECT_KEY method for key derivation and IV generation.  Previously,
this method was implicitly detected via --mode-num being given without
either --iv-ino-lblk-64 or --iv-ino-lblk-32.

The benefit of this change is that it makes the various options to
fscrypt-crypt-util behave more consistently.  --direct-key,
--iv-ino-lblk-64, and --iv-ino-lblk-32 now all work similarly (they
select a key derivation and IV generation method); likewise, --mode-num,
--file-nonce, --inode-number, and --fs-uuid now all work similarly (they
provide information that key derivation and IV generation may need).

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

diff --git a/common/encrypt b/common/encrypt
index f90c4ef0..2cf02ca0 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -842,27 +842,25 @@ _verify_ciphertext_for_encryption_policy()
 
 	set_encpolicy_args+=" -c $contents_mode_num"
 	set_encpolicy_args+=" -n $filenames_mode_num"
+	crypt_util_contents_args+=" --mode-num=$contents_mode_num"
+	crypt_util_filename_args+=" --mode-num=$filenames_mode_num"
 
 	if (( policy_version > 1 )); then
 		set_encpolicy_args+=" -v 2"
 		crypt_util_args+=" --kdf=HKDF-SHA512"
 		if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
-			crypt_util_args+=" --mode-num=$contents_mode_num"
+			crypt_util_args+=" --direct-key"
 		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"
 		elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 )); then
 			crypt_util_args+=" --iv-ino-lblk-32"
-			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"
+			crypt_util_args+=" --direct-key --kdf=none"
 		else
 			crypt_util_args+=" --kdf=AES-128-ECB"
 		fi
diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index 03cc3c4a..022ff7bd 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -64,6 +64,8 @@ static void usage(FILE *fp)
 "  --block-size=BLOCK_SIZE     Encrypt each BLOCK_SIZE bytes independently.\n"
 "                                Default: 4096 bytes\n"
 "  --decrypt                   Decrypt instead of encrypt\n"
+"  --direct-key                Use the format where the IVs include the file\n"
+"                                nonce and the same key is shared across files.\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"
 "                                Required for --iv-ino-lblk-32 and\n"
@@ -76,11 +78,10 @@ static void usage(FILE *fp)
 "                                32-bit variant.\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"
+"  --mode-num=NUM              The encryption mode number.  This may be required\n"
+"                                for key derivation, depending on other options.\n"
 "  --padding=PADDING           If last block is partial, zero-pad it to next\n"
 "                                PADDING-byte boundary.  Default: BLOCK_SIZE\n"
 	, fp);
@@ -1790,6 +1791,7 @@ struct key_and_iv_params {
 	u8 mode_num;
 	u8 file_nonce[FILE_NONCE_SIZE];
 	bool file_nonce_specified;
+	bool direct_key;
 	bool iv_ino_lblk_64;
 	bool iv_ino_lblk_32;
 	u64 block_number;
@@ -1835,7 +1837,7 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 			   u8 *real_key, size_t real_key_size,
 			   union fscrypt_iv *iv)
 {
-	bool file_nonce_in_iv = false;
+	int iv_methods = 0;
 	struct aes_key aes_key;
 	u8 info[8 + 1 + 1 + UUID_SIZE] = "fscrypt";
 	size_t infolen = 8;
@@ -1848,11 +1850,22 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 	/* Overridden later for iv_ino_lblk_{64,32} */
 	iv->block_number = cpu_to_le64(params->block_number);
 
-	if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) {
+	iv_methods += params->direct_key;
+	iv_methods += params->iv_ino_lblk_64;
+	iv_methods += params->iv_ino_lblk_32;
+	if (iv_methods > 1)
+		die("Conflicting IV methods specified");
+	if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB)
+		die("--kdf=AES-128-ECB is incompatible with IV method options");
+
+	if (params->direct_key) {
+		if (!params->file_nonce_specified)
+			die("--direct-key requires file nonce");
+		if (params->kdf != KDF_NONE && params->mode_num == 0)
+			die("--direct-key with KDF requires --mode-num");
+	} else if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) {
 		const char *opt = params->iv_ino_lblk_64 ? "--iv-ino-lblk-64" :
 							   "--iv-ino-lblk-32";
-		if (params->iv_ino_lblk_64 && params->iv_ino_lblk_32)
-			die("--iv-ino-lblk-64 and --iv-ino-lblk-32 are mutually exclusive");
 		if (params->kdf != KDF_HKDF_SHA512)
 			die("%s requires --kdf=HKDF-SHA512", opt);
 		if (!params->fs_uuid_specified)
@@ -1869,16 +1882,11 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 
 	switch (params->kdf) {
 	case KDF_NONE:
-		if (params->mode_num != 0)
-			die("--mode-num isn't supported with --kdf=none");
 		memcpy(real_key, params->master_key, real_key_size);
-		file_nonce_in_iv = true;
 		break;
 	case KDF_AES_128_ECB:
 		if (!params->file_nonce_specified)
-			die("--file-nonce is required with --kdf=AES-128-ECB");
-		if (params->mode_num != 0)
-			die("--mode-num isn't supported with --kdf=AES-128-ECB");
+			die("--kdf=AES-128-ECB requires --file-nonce");
 		STATIC_ASSERT(FILE_NONCE_SIZE == AES_128_KEY_SIZE);
 		ASSERT(real_key_size % AES_BLOCK_SIZE == 0);
 		aes_setkey(&aes_key, params->file_nonce, AES_128_KEY_SIZE);
@@ -1887,7 +1895,10 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 				    &real_key[i]);
 		break;
 	case KDF_HKDF_SHA512:
-		if (params->iv_ino_lblk_64) {
+		if (params->direct_key) {
+			info[infolen++] = HKDF_CONTEXT_DIRECT_KEY;
+			info[infolen++] = params->mode_num;
+		} else if (params->iv_ino_lblk_64) {
 			info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_64_KEY;
 			info[infolen++] = params->mode_num;
 			memcpy(&info[infolen], params->fs_uuid, UUID_SIZE);
@@ -1903,17 +1914,13 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 				cpu_to_le32(hash_inode_number(params) +
 					    params->block_number);
 			iv->inode_number = 0;
-		} 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) {
 			info[infolen++] = HKDF_CONTEXT_PER_FILE_ENC_KEY;
 			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");
+			die("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-*");
 		}
 		hkdf_sha512(params->master_key, params->master_key_size,
 			    NULL, 0, info, infolen, real_key, real_key_size);
@@ -1922,7 +1929,7 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 		ASSERT(0);
 	}
 
-	if (file_nonce_in_iv && params->file_nonce_specified)
+	if (params->direct_key)
 		memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE);
 }
 
@@ -1930,6 +1937,7 @@ enum {
 	OPT_BLOCK_NUMBER,
 	OPT_BLOCK_SIZE,
 	OPT_DECRYPT,
+	OPT_DIRECT_KEY,
 	OPT_FILE_NONCE,
 	OPT_FS_UUID,
 	OPT_HELP,
@@ -1945,6 +1953,7 @@ static const struct option longopts[] = {
 	{ "block-number",    required_argument, NULL, OPT_BLOCK_NUMBER },
 	{ "block-size",      required_argument, NULL, OPT_BLOCK_SIZE },
 	{ "decrypt",         no_argument,       NULL, OPT_DECRYPT },
+	{ "direct-key",      no_argument,       NULL, OPT_DIRECT_KEY },
 	{ "file-nonce",      required_argument, NULL, OPT_FILE_NONCE },
 	{ "fs-uuid",         required_argument, NULL, OPT_FS_UUID },
 	{ "help",            no_argument,       NULL, OPT_HELP },
@@ -1999,6 +2008,9 @@ int main(int argc, char *argv[])
 		case OPT_DECRYPT:
 			decrypting = true;
 			break;
+		case OPT_DIRECT_KEY:
+			params.direct_key = true;
+			break;
 		case OPT_FILE_NONCE:
 			if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE)
 			    != FILE_NONCE_SIZE)
-- 
2.35.1


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

* [RFC PATCH 2/8] fscrypt-crypt-util: refactor get_key_and_iv()
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 3/8] fscrypt-crypt-util: add support for dumping key identifier Eric Biggers
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

Split get_key_and_iv() into two distinct parts: (1) deriving the key and
(2) generating the IV.  Also, check for the presence of needed options
just before they are used rather than doing it all up-front.

These changes should make this code much easier to understand.

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

diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index 022ff7bd..0ecf9272 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -1828,16 +1828,9 @@ static u32 hash_inode_number(const struct key_and_iv_params *params)
 	return (u32)siphash_1u64(hash_key.words, params->inode_number);
 }
 
-/*
- * 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 struct key_and_iv_params *params,
-			   u8 *real_key, size_t real_key_size,
-			   union fscrypt_iv *iv)
+static void derive_real_key(const struct key_and_iv_params *params,
+			    u8 *real_key, size_t real_key_size)
 {
-	int iv_methods = 0;
 	struct aes_key aes_key;
 	u8 info[8 + 1 + 1 + UUID_SIZE] = "fscrypt";
 	size_t infolen = 8;
@@ -1845,41 +1838,6 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 
 	ASSERT(real_key_size <= params->master_key_size);
 
-	memset(iv, 0, sizeof(*iv));
-
-	/* Overridden later for iv_ino_lblk_{64,32} */
-	iv->block_number = cpu_to_le64(params->block_number);
-
-	iv_methods += params->direct_key;
-	iv_methods += params->iv_ino_lblk_64;
-	iv_methods += params->iv_ino_lblk_32;
-	if (iv_methods > 1)
-		die("Conflicting IV methods specified");
-	if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB)
-		die("--kdf=AES-128-ECB is incompatible with IV method options");
-
-	if (params->direct_key) {
-		if (!params->file_nonce_specified)
-			die("--direct-key requires file nonce");
-		if (params->kdf != KDF_NONE && params->mode_num == 0)
-			die("--direct-key with KDF requires --mode-num");
-	} else if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) {
-		const char *opt = params->iv_ino_lblk_64 ? "--iv-ino-lblk-64" :
-							   "--iv-ino-lblk-32";
-		if (params->kdf != KDF_HKDF_SHA512)
-			die("%s requires --kdf=HKDF-SHA512", opt);
-		if (!params->fs_uuid_specified)
-			die("%s requires --fs-uuid", opt);
-		if (params->inode_number == 0)
-			die("%s requires --inode-number", opt);
-		if (params->mode_num == 0)
-			die("%s requires --mode-num", opt);
-		if (params->block_number > UINT32_MAX)
-			die("%s can't use --block-number > UINT32_MAX", opt);
-		if (params->inode_number > UINT32_MAX)
-			die("%s can't use --inode-number > UINT32_MAX", opt);
-	}
-
 	switch (params->kdf) {
 	case KDF_NONE:
 		memcpy(real_key, params->master_key, real_key_size);
@@ -1896,24 +1854,28 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 		break;
 	case KDF_HKDF_SHA512:
 		if (params->direct_key) {
+			if (params->mode_num == 0)
+				die("--direct-key requires --mode-num");
 			info[infolen++] = HKDF_CONTEXT_DIRECT_KEY;
 			info[infolen++] = params->mode_num;
 		} else if (params->iv_ino_lblk_64) {
+			if (params->mode_num == 0)
+				die("--iv-ino-lblk-64 requires --mode-num");
+			if (!params->fs_uuid_specified)
+				die("--iv-ino-lblk-64 requires --fs-uuid");
 			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;
-			iv->block_number32 = cpu_to_le32(params->block_number);
-			iv->inode_number = cpu_to_le32(params->inode_number);
 		} else if (params->iv_ino_lblk_32) {
+			if (params->mode_num == 0)
+				die("--iv-ino-lblk-32 requires --mode-num");
+			if (!params->fs_uuid_specified)
+				die("--iv-ino-lblk-32 requires --fs-uuid");
 			info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_32_KEY;
 			info[infolen++] = params->mode_num;
 			memcpy(&info[infolen], params->fs_uuid, UUID_SIZE);
 			infolen += UUID_SIZE;
-			iv->block_number32 =
-				cpu_to_le32(hash_inode_number(params) +
-					    params->block_number);
-			iv->inode_number = 0;
 		} else if (params->file_nonce_specified) {
 			info[infolen++] = HKDF_CONTEXT_PER_FILE_ENC_KEY;
 			memcpy(&info[infolen], params->file_nonce,
@@ -1928,9 +1890,60 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 	default:
 		ASSERT(0);
 	}
+}
 
-	if (params->direct_key)
+static void generate_iv(const struct key_and_iv_params *params,
+			union fscrypt_iv *iv)
+{
+	memset(iv, 0, sizeof(*iv));
+	if (params->direct_key) {
+		if (!params->file_nonce_specified)
+			die("--direct-key requires file nonce");
+		iv->block_number = cpu_to_le64(params->block_number);
 		memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE);
+	} else if (params->iv_ino_lblk_64) {
+		if (params->block_number > UINT32_MAX)
+			die("iv-ino-lblk-64 can't use --block-number > UINT32_MAX");
+		if (params->inode_number == 0)
+			die("iv-ino-lblk-64 requires --inode-number");
+		if (params->inode_number > UINT32_MAX)
+			die("iv-ino-lblk-64 can't use --inode-number > UINT32_MAX");
+		iv->block_number32 = cpu_to_le32(params->block_number);
+		iv->inode_number = cpu_to_le32(params->inode_number);
+	} else if (params->iv_ino_lblk_32) {
+		if (params->block_number > UINT32_MAX)
+			die("iv-ino-lblk-32 can't use --block-number > UINT32_MAX");
+		if (params->inode_number == 0)
+			die("iv-ino-lblk-32 requires --inode-number");
+		iv->block_number32 = cpu_to_le32(hash_inode_number(params) +
+						 params->block_number);
+	} else {
+		iv->block_number = cpu_to_le64(params->block_number);
+	}
+}
+
+/*
+ * Get the key and starting IV with which the encryption will actually be done.
+ * If a KDF was specified, then a subkey is derived from the master key.
+ * Otherwise, the master key is used directly.
+ */
+static void get_key_and_iv(const struct key_and_iv_params *params,
+			   u8 *real_key, size_t real_key_size,
+			   union fscrypt_iv *iv)
+{
+	int iv_methods = 0;
+
+	iv_methods += params->direct_key;
+	iv_methods += params->iv_ino_lblk_64;
+	iv_methods += params->iv_ino_lblk_32;
+	if (iv_methods > 1)
+		die("Conflicting IV methods specified");
+	if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB)
+		die("--kdf=AES-128-ECB is incompatible with IV method options");
+
+	derive_real_key(params, real_key, real_key_size);
+
+	generate_iv(params, iv);
 }
 
 enum {
-- 
2.35.1


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

* [RFC PATCH 3/8] fscrypt-crypt-util: add support for dumping key identifier
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 2/8] fscrypt-crypt-util: refactor get_key_and_iv() Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 4/8] common/encrypt: log full ciphertext verification params Eric Biggers
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

Add an option to fscrypt-crypt-util to make it compute the key
identifier for the given key.  This will allow testing the correctness
of the filesystem's key identifier computation.

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

diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index 0ecf9272..876d9f5c 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -46,7 +46,7 @@
 static void usage(FILE *fp)
 {
 	fputs(
-"Usage: " PROGRAM_NAME " [OPTION]... CIPHER MASTER_KEY\n"
+"Usage: " PROGRAM_NAME " [OPTION]... [CIPHER | --dump-key-identifier] MASTER_KEY\n"
 "\n"
 "Utility for verifying fscrypt-encrypted data.  This program encrypts\n"
 "(or decrypts) the data on stdin using the given CIPHER with the given\n"
@@ -66,6 +66,8 @@ static void usage(FILE *fp)
 "  --decrypt                   Decrypt instead of encrypt\n"
 "  --direct-key                Use the format where the IVs include the file\n"
 "                                nonce and the same key is shared across files.\n"
+"  --dump-key-identifier       Instead of encrypting/decrypting data, just\n"
+"                                compute and dump the key identifier.\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"
 "                                Required for --iv-ino-lblk-32 and\n"
@@ -1946,11 +1948,31 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 	generate_iv(params, iv);
 }
 
+static void do_dump_key_identifier(const struct key_and_iv_params *params)
+{
+	u8 info[9] = "fscrypt";
+	u8 key_identifier[16];
+	int i;
+
+	if (params->kdf != KDF_HKDF_SHA512)
+		die("--dump-key-identifier requires --kdf=HKDF-SHA512");
+
+	info[8] = HKDF_CONTEXT_KEY_IDENTIFIER;
+
+	hkdf_sha512(params->master_key, params->master_key_size,
+		    NULL, 0, info, sizeof(info), key_identifier,
+		    sizeof(key_identifier));
+
+	for (i = 0; i < sizeof(key_identifier); i++)
+		printf("%02x", key_identifier[i]);
+}
+
 enum {
 	OPT_BLOCK_NUMBER,
 	OPT_BLOCK_SIZE,
 	OPT_DECRYPT,
 	OPT_DIRECT_KEY,
+	OPT_DUMP_KEY_IDENTIFIER,
 	OPT_FILE_NONCE,
 	OPT_FS_UUID,
 	OPT_HELP,
@@ -1967,6 +1989,7 @@ static const struct option longopts[] = {
 	{ "block-size",      required_argument, NULL, OPT_BLOCK_SIZE },
 	{ "decrypt",         no_argument,       NULL, OPT_DECRYPT },
 	{ "direct-key",      no_argument,       NULL, OPT_DIRECT_KEY },
+	{ "dump-key-identifier", no_argument,   NULL, OPT_DUMP_KEY_IDENTIFIER },
 	{ "file-nonce",      required_argument, NULL, OPT_FILE_NONCE },
 	{ "fs-uuid",         required_argument, NULL, OPT_FS_UUID },
 	{ "help",            no_argument,       NULL, OPT_HELP },
@@ -1983,9 +2006,10 @@ int main(int argc, char *argv[])
 {
 	size_t block_size = 4096;
 	bool decrypting = false;
+	bool dump_key_identifier = false;
 	struct key_and_iv_params params;
 	size_t padding = 0;
-	const struct fscrypt_cipher *cipher;
+	const struct fscrypt_cipher *cipher = NULL;
 	u8 real_key[MAX_KEY_SIZE];
 	union fscrypt_iv iv;
 	char *tmp;
@@ -2024,6 +2048,9 @@ int main(int argc, char *argv[])
 		case OPT_DIRECT_KEY:
 			params.direct_key = true;
 			break;
+		case OPT_DUMP_KEY_IDENTIFIER:
+			dump_key_identifier = true;
+			break;
 		case OPT_FILE_NONCE:
 			if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE)
 			    != FILE_NONCE_SIZE)
@@ -2071,29 +2098,40 @@ int main(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
-	if (argc != 2) {
-		usage(stderr);
-		return 2;
+	if (dump_key_identifier) {
+		if (argc != 1) {
+			usage(stderr);
+			return 2;
+		}
+	} else {
+		if (argc != 2) {
+			usage(stderr);
+			return 2;
+		}
+		cipher = find_fscrypt_cipher(*argv);
+		if (cipher == NULL)
+			die("Unknown cipher: %s", *argv);
+
+		if (block_size < cipher->min_input_size)
+			die("Block size of %zu bytes is too small for cipher %s",
+			    block_size, cipher->name);
+		argv++;
 	}
-
-	cipher = find_fscrypt_cipher(argv[0]);
-	if (cipher == NULL)
-		die("Unknown cipher: %s", argv[0]);
-
-	if (block_size < cipher->min_input_size)
-		die("Block size of %zu bytes is too small for cipher %s",
-		    block_size, cipher->name);
-
-	params.master_key_size = hex2bin(argv[1], params.master_key,
+	params.master_key_size = hex2bin(*argv, params.master_key,
 					 MAX_KEY_SIZE);
 	if (params.master_key_size < 0)
-		die("Invalid master_key: %s", argv[1]);
-	if (params.master_key_size < cipher->keysize)
-		die("Master key is too short for cipher %s", cipher->name);
+		die("Invalid master_key: %s", *argv);
 
-	get_key_and_iv(&params, real_key, cipher->keysize, &iv);
-
-	crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding,
-		   params.iv_ino_lblk_64 || params.iv_ino_lblk_32);
+	if (dump_key_identifier) {
+		do_dump_key_identifier(&params);
+	} else {
+		if (params.master_key_size < cipher->keysize)
+			die("Master key is too short for cipher %s",
+			    cipher->name);
+		get_key_and_iv(&params, real_key, cipher->keysize, &iv);
+		crypt_loop(cipher, real_key, &iv, decrypting, block_size,
+			   padding,
+			   params.iv_ino_lblk_64 || params.iv_ino_lblk_32);
+	}
 	return 0;
 }
-- 
2.35.1


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

* [RFC PATCH 4/8] common/encrypt: log full ciphertext verification params
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
                   ` (2 preceding siblings ...)
  2022-02-28  7:47 ` [RFC PATCH 3/8] fscrypt-crypt-util: add support for dumping key identifier Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 5/8] common/encrypt: verify the key identifiers Eric Biggers
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

To help with debugging, log some additional information to $seqres.full.

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

diff --git a/common/encrypt b/common/encrypt
index 2cf02ca0..cf402570 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -908,6 +908,17 @@ _verify_ciphertext_for_encryption_policy()
 	echo -e "\tfilenames_encryption_mode: $filenames_encryption_mode"
 	[ $# -ne 0 ] && echo -e "\toptions: $*"
 
+	cat >> $seqres.full <<EOF
+Full ciphertext verification parameters:
+  contents_encryption_mode = $contents_encryption_mode
+  filenames_encryption_mode = $filenames_encryption_mode
+  policy_flags = $policy_flags
+  set_encpolicy_args = $set_encpolicy_args
+  keyspec = $keyspec
+  raw_key_hex = $raw_key_hex
+  crypt_util_contents_args = $crypt_util_contents_args
+  crypt_util_filename_args = $crypt_util_filename_args
+EOF
 	_do_verify_ciphertext_for_encryption_policy \
 		"$contents_encryption_mode" \
 		"$filenames_encryption_mode" \
-- 
2.35.1


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

* [RFC PATCH 5/8] common/encrypt: verify the key identifiers
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
                   ` (3 preceding siblings ...)
  2022-02-28  7:47 ` [RFC PATCH 4/8] common/encrypt: log full ciphertext verification params Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 6/8] fscrypt-crypt-util: add hardware KDF support Eric Biggers
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

As part of all the ciphertext verification tests, verify that the
filesystem correctly computed the key identifier from the key the test
generated.  This uses fscrypt-crypt-util to compute the key identifier.

Previously this was only being tested indirectly, via the tests that
happen to use the hardcoded $TEST_RAW_KEY and $TEST_KEY_IDENTIFIER.
The new check provides better coverage.

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

diff --git a/common/encrypt b/common/encrypt
index cf402570..d8e2dba9 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -902,6 +902,18 @@ _verify_ciphertext_for_encryption_policy()
 	fi
 	local raw_key_hex=$(echo "$raw_key" | tr -d '\\x')
 
+	if (( policy_version > 1 )); then
+		echo "Verifying key identifier" >> $seqres.full
+		expected_identifier=$($here/src/fscrypt-crypt-util  \
+				      --dump-key-identifier "$raw_key_hex" \
+				      $crypt_util_args)
+		if [ "$expected_identifier" != "$keyspec" ]; then
+			echo "KEY IDENTIFIER MISMATCH!"
+			echo "    Expected: $expected_identifier"
+			echo "    Actual: $keyspec"
+		fi
+	fi
+
 	echo
 	echo -e "Verifying ciphertext with parameters:"
 	echo -e "\tcontents_encryption_mode: $contents_encryption_mode"
-- 
2.35.1


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

* [RFC PATCH 6/8] fscrypt-crypt-util: add hardware KDF support
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
                   ` (4 preceding siblings ...)
  2022-02-28  7:47 ` [RFC PATCH 5/8] common/encrypt: verify the key identifiers Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 7/8] common/encrypt: support hardware-wrapped key testing Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 8/8] generic: verify ciphertext with hardware-wrapped keys Eric Biggers
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

Add support to fscrypt-crypt-util for replicating the extra KDF (Key
Derivation Function) step that is required when a hardware-wrapped key
is used.  This step normally occurs in hardware, but we need to
replicate it for testing purposes.

Note, some care was needed to handle the fact that both inlinecrypt_key
and sw_secret can be needed in a single run of fscrypt-crypt-util.
Namely, with --iv-ino-lblk-32, inlinecrypt_key is needed for the
en/decryption while sw_secret is needed for hash_inode_number().

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

diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c
index 876d9f5c..7c19bb90 100644
--- a/src/fscrypt-crypt-util.c
+++ b/src/fscrypt-crypt-util.c
@@ -68,6 +68,10 @@ static void usage(FILE *fp)
 "                                nonce and the same key is shared across files.\n"
 "  --dump-key-identifier       Instead of encrypting/decrypting data, just\n"
 "                                compute and dump the key identifier.\n"
+"  --enable-hw-kdf             Apply the hardware KDF (replicated in software)\n"
+"                                to the key before using it.  Use this to\n"
+"                                replicate the en/decryption that is done when\n"
+"                                the filesystem is given a hardware-wrapped key.\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"
 "                                Required for --iv-ino-lblk-32 and\n"
@@ -86,6 +90,10 @@ static void usage(FILE *fp)
 "                                for key derivation, depending on other options.\n"
 "  --padding=PADDING           If last block is partial, zero-pad it to next\n"
 "                                PADDING-byte boundary.  Default: BLOCK_SIZE\n"
+"  --use-inlinecrypt-key       In combination with --enable-hw-kdf, this causes\n"
+"                                the en/decryption to be done with the \"inline\n"
+"                                encryption key\" rather than with a key derived\n"
+"                                from the \"software secret\".\n"
 	, fp);
 }
 
@@ -295,6 +303,12 @@ typedef struct {
 	__le64 hi;
 } ble128;
 
+/* Same as ble128 but with big-endian byte order */
+typedef struct {
+	__be64 hi;
+	__be64 lo;
+} bbe128;
+
 /* Multiply a GF(2^128) element by the polynomial 'x' */
 static inline void gf2_128_mul_x(ble128 *t)
 {
@@ -305,6 +319,16 @@ static inline void gf2_128_mul_x(ble128 *t)
 	t->lo = cpu_to_le64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0));
 }
 
+/* Same as gf2_128_mul_x() but with big-endian byte order */
+static inline void gf2_128_mul_x_be(bbe128 *t)
+{
+	u64 lo = be64_to_cpu(t->lo);
+	u64 hi = be64_to_cpu(t->hi);
+
+	t->hi = cpu_to_be64((hi << 1) | (lo >> 63));
+	t->lo = cpu_to_be64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0));
+}
+
 /*----------------------------------------------------------------------------*
  *                             Group arithmetic                               *
  *----------------------------------------------------------------------------*/
@@ -1176,6 +1200,67 @@ static void aes_128_cts_cbc_decrypt(const u8 key[AES_128_KEY_SIZE],
 	aes_cts_cbc_decrypt(key, AES_128_KEY_SIZE, iv, src, dst, nbytes);
 }
 
+static void aes_256_cmac(const u8 key[AES_256_KEY_SIZE],
+			 const u8 *msg, size_t msglen, u8 mac[AES_BLOCK_SIZE])
+{
+	const size_t partial = msglen % AES_BLOCK_SIZE;
+	const size_t full_blocks = msglen ? (msglen - 1) / AES_BLOCK_SIZE : 0;
+	struct aes_key k;
+	bbe128 subkey;
+	size_t i;
+
+	aes_setkey(&k, key, AES_256_KEY_SIZE);
+	memset(&subkey, 0, sizeof(subkey));
+	aes_encrypt(&k, (u8 *)&subkey, (u8 *)&subkey);
+	gf2_128_mul_x_be(&subkey);
+
+	memset(mac, 0, AES_BLOCK_SIZE);
+	for (i = 0; i < full_blocks * AES_BLOCK_SIZE; i += AES_BLOCK_SIZE) {
+		xor(mac, mac, &msg[i], AES_BLOCK_SIZE);
+		aes_encrypt(&k, mac, mac);
+	}
+	xor(mac, mac, &msg[i], msglen - i);
+	if (partial != 0 || msglen == 0) {
+		mac[msglen - i] ^= 0x80;
+		gf2_128_mul_x_be(&subkey);
+	}
+	xor(mac, mac, (u8 *)&subkey, AES_BLOCK_SIZE);
+	aes_encrypt(&k, mac, mac);
+}
+
+#ifdef ENABLE_ALG_TESTS
+#include <openssl/cmac.h>
+static void test_aes_256_cmac(void)
+{
+	unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+	CMAC_CTX *ctx = CMAC_CTX_new();
+
+	ASSERT(ctx != NULL);
+	while (num_tests--) {
+		u8 key[AES_256_KEY_SIZE];
+		u8 msg[128];
+		u8 mac[AES_BLOCK_SIZE];
+		u8 ref_mac[sizeof(mac)];
+		const size_t msglen = 1 + (rand() % sizeof(msg));
+		size_t out_len = 0;
+
+		rand_bytes(key, sizeof(key));
+		rand_bytes(msg, msglen);
+
+		aes_256_cmac(key, msg, msglen, mac);
+
+		ASSERT(ctx != NULL);
+		ASSERT(CMAC_Init(ctx, key, sizeof(key), EVP_aes_256_cbc(),
+				 NULL) == 1);
+		ASSERT(CMAC_Update(ctx, msg, msglen) == 1);
+		ASSERT(CMAC_Final(ctx, ref_mac, &out_len));
+		ASSERT(out_len == sizeof(mac));
+		ASSERT(memcmp(mac, ref_mac, sizeof(mac)) == 0);
+	}
+	CMAC_CTX_free(ctx);
+}
+#endif /* ENABLE_ALG_TESTS */
+
 /*----------------------------------------------------------------------------*
  *                           XChaCha12 stream cipher                          *
  *----------------------------------------------------------------------------*/
@@ -1787,9 +1872,17 @@ static u8 parse_mode_number(const char *arg)
 }
 
 struct key_and_iv_params {
+	/*
+	 * If enable_hw_kdf=true, then master_key and sw_secret will differ.
+	 * Otherwise they will be the same.
+	 */
 	u8 master_key[MAX_KEY_SIZE];
 	int master_key_size;
+	u8 sw_secret[MAX_KEY_SIZE];
+	int sw_secret_size;
 	enum kdf_algorithm kdf;
+	bool enable_hw_kdf;
+	bool use_inlinecrypt_key;
 	u8 mode_num;
 	u8 file_nonce[FILE_NONCE_SIZE];
 	bool file_nonce_specified;
@@ -1802,13 +1895,14 @@ struct key_and_iv_params {
 	bool fs_uuid_specified;
 };
 
-#define HKDF_CONTEXT_KEY_IDENTIFIER	1
+#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY 1
 #define HKDF_CONTEXT_PER_FILE_ENC_KEY	2
 #define HKDF_CONTEXT_DIRECT_KEY		3
 #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY	4
 #define HKDF_CONTEXT_DIRHASH_KEY	5
 #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY	6
 #define HKDF_CONTEXT_INODE_HASH_KEY	7
+#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY 8
 
 /* Hash the file's inode number using SipHash keyed by a derived key */
 static u32 hash_inode_number(const struct key_and_iv_params *params)
@@ -1820,7 +1914,7 @@ static u32 hash_inode_number(const struct key_and_iv_params *params)
 	} hash_key;
 
 	info[8] = HKDF_CONTEXT_INODE_HASH_KEY;
-	hkdf_sha512(params->master_key, params->master_key_size,
+	hkdf_sha512(params->sw_secret, params->sw_secret_size,
 		    NULL, 0, info, sizeof(info),
 		    hash_key.bytes, sizeof(hash_key));
 
@@ -1830,6 +1924,102 @@ static u32 hash_inode_number(const struct key_and_iv_params *params)
 	return (u32)siphash_1u64(hash_key.words, params->inode_number);
 }
 
+/*
+ * Replicate the hardware KDF, given the raw master key and the context
+ * indicating which type of key to derive.
+ *
+ * Detailed explanation:
+ *
+ * With hardware-wrapped keys, an extra level is inserted into fscrypt's key
+ * hierarchy, above what was previously the root:
+ *
+ *                           master_key
+ *                               |
+ *                         -------------
+ *                         |            |
+ *                         |            |
+ *                inlinecrypt_key    sw_secret
+ *                                      |
+ *                                      |
+ *                                (everything else)
+ *
+ * From the master key, the "inline encryption key" (inlinecrypt_key) and
+ * "software secret" (sw_secret) are derived.  The inlinecrypt_key is used to
+ * encrypt file contents.  The sw_secret is used just like the old master key,
+ * except that it isn't used to derive the file contents key.  (I.e., it's used
+ * to derive filenames encryption keys, key identifiers, inode hash keys, etc.)
+ *
+ * Normally, software only sees master_key in "wrapped" form, and can never see
+ * inlinecrypt_key at all.  Only specialized hardware can access the raw
+ * master_key to derive the subkeys, in a step we call the "HW KDF".
+ *
+ * However, the HW KDF is a well-specified algorithm, and when a
+ * hardware-wrapped key is initially created, software can choose to import a
+ * raw key.  This allows software to test the feature by replicating the HW KDF.
+ *
+ * This is what this function does; it will derive either the inlinecrypt_key
+ * key or the sw_secret, depending on the KDF context passed.
+ */
+static void hw_kdf(const u8 *master_key, size_t master_key_size,
+		   const u8 *ctx, size_t ctx_size,
+		   u8 *derived_key, size_t derived_key_size)
+{
+	static const u8 label[11] = "\0\0\x40\0\0\0\0\0\0\0\x20";
+	u8 info[128];
+	size_t i;
+
+	if (master_key_size != AES_256_KEY_SIZE)
+		die("--hw-kdf requires a 32-byte master key");
+	ASSERT(derived_key_size % AES_BLOCK_SIZE == 0);
+
+	/*
+	 * This is NIST SP 800-108 "KDF in Counter Mode" with AES-256-CMAC as
+	 * the PRF and a particular choice of labels and contexts.
+	 */
+	for (i = 0; i < derived_key_size; i += AES_BLOCK_SIZE) {
+		u8 *p = info;
+
+		ASSERT(sizeof(__be32) + sizeof(label) + 1 + ctx_size +
+		       sizeof(__be32) <= sizeof(info));
+
+		put_unaligned_be32(1 + (i / AES_BLOCK_SIZE), p);
+		p += sizeof(__be32);
+		memcpy(p, label, sizeof(label));
+		p += sizeof(label);
+		*p++ = 0;
+		memcpy(p, ctx, ctx_size);
+		p += ctx_size;
+		put_unaligned_be32(derived_key_size * 8, p);
+		p += sizeof(__be32);
+
+		aes_256_cmac(master_key, info, p - info, &derived_key[i]);
+	}
+}
+
+#define INLINECRYPT_KEY_SIZE 64
+#define SW_SECRET_SIZE 32
+
+static void derive_inline_encryption_key(const u8 *master_key,
+					 size_t master_key_size,
+					 u8 inlinecrypt_key[INLINECRYPT_KEY_SIZE])
+{
+	static const u8 ctx[36] =
+		"inline encryption key\0\0\0\0\0\0\x03\x43\0\x82\x50\0\0\0\0";
+
+	hw_kdf(master_key, master_key_size, ctx, sizeof(ctx),
+	       inlinecrypt_key, INLINECRYPT_KEY_SIZE);
+}
+
+static void derive_sw_secret(const u8 *master_key, size_t master_key_size,
+			     u8 sw_secret[SW_SECRET_SIZE])
+{
+	static const u8 ctx[28] =
+		"raw secret\0\0\0\0\0\0\0\0\0\x03\x17\0\x80\x50\0\0\0\0";
+
+	hw_kdf(master_key, master_key_size, ctx, sizeof(ctx),
+	       sw_secret, SW_SECRET_SIZE);
+}
+
 static void derive_real_key(const struct key_and_iv_params *params,
 			    u8 *real_key, size_t real_key_size)
 {
@@ -1838,11 +2028,30 @@ static void derive_real_key(const struct key_and_iv_params *params,
 	size_t infolen = 8;
 	size_t i;
 
-	ASSERT(real_key_size <= params->master_key_size);
+	if (params->use_inlinecrypt_key) {
+		/*
+		 * With --use-inlinecrypt-key, we need to use the "hardware KDF"
+		 * rather than the normal fscrypt KDF.  Note that the fscrypt
+		 * KDF might still be used elsewhere, e.g. hash_inode_number()
+		 * -- it just won't be used for the actual encryption key.
+		 */
+		if (!params->enable_hw_kdf)
+			die("--use-inlinecrypt-key requires --enable-hw-kdf");
+		if (!params->iv_ino_lblk_64 && !params->iv_ino_lblk_32)
+			die("--use-inlinecrypt-key requires one of --iv-ino-lblk-*");
+		if (real_key_size != INLINECRYPT_KEY_SIZE)
+			die("cipher not compatible with --use-inlinecrypt-key");
+		derive_inline_encryption_key(params->master_key,
+					     params->master_key_size, real_key);
+		return;
+	}
+
+	if (params->sw_secret_size < real_key_size)
+		die("Master key is too short for cipher");
 
 	switch (params->kdf) {
 	case KDF_NONE:
-		memcpy(real_key, params->master_key, real_key_size);
+		memcpy(real_key, params->sw_secret, real_key_size);
 		break;
 	case KDF_AES_128_ECB:
 		if (!params->file_nonce_specified)
@@ -1851,7 +2060,7 @@ static void derive_real_key(const struct key_and_iv_params *params,
 		ASSERT(real_key_size % AES_BLOCK_SIZE == 0);
 		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, &params->master_key[i],
+			aes_encrypt(&aes_key, &params->sw_secret[i],
 				    &real_key[i]);
 		break;
 	case KDF_HKDF_SHA512:
@@ -1886,7 +2095,7 @@ static void derive_real_key(const struct key_and_iv_params *params,
 		} else {
 			die("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-*");
 		}
-		hkdf_sha512(params->master_key, params->master_key_size,
+		hkdf_sha512(params->sw_secret, params->sw_secret_size,
 			    NULL, 0, info, infolen, real_key, real_key_size);
 		break;
 	default:
@@ -1957,9 +2166,12 @@ static void do_dump_key_identifier(const struct key_and_iv_params *params)
 	if (params->kdf != KDF_HKDF_SHA512)
 		die("--dump-key-identifier requires --kdf=HKDF-SHA512");
 
-	info[8] = HKDF_CONTEXT_KEY_IDENTIFIER;
+	if (params->enable_hw_kdf)
+		info[8] = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY;
+	else
+		info[8] = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY;
 
-	hkdf_sha512(params->master_key, params->master_key_size,
+	hkdf_sha512(params->sw_secret, params->sw_secret_size,
 		    NULL, 0, info, sizeof(info), key_identifier,
 		    sizeof(key_identifier));
 
@@ -1973,6 +2185,7 @@ enum {
 	OPT_DECRYPT,
 	OPT_DIRECT_KEY,
 	OPT_DUMP_KEY_IDENTIFIER,
+	OPT_ENABLE_HW_KDF,
 	OPT_FILE_NONCE,
 	OPT_FS_UUID,
 	OPT_HELP,
@@ -1982,6 +2195,7 @@ enum {
 	OPT_KDF,
 	OPT_MODE_NUM,
 	OPT_PADDING,
+	OPT_USE_INLINECRYPT_KEY,
 };
 
 static const struct option longopts[] = {
@@ -1990,6 +2204,7 @@ static const struct option longopts[] = {
 	{ "decrypt",         no_argument,       NULL, OPT_DECRYPT },
 	{ "direct-key",      no_argument,       NULL, OPT_DIRECT_KEY },
 	{ "dump-key-identifier", no_argument,   NULL, OPT_DUMP_KEY_IDENTIFIER },
+	{ "enable-hw-kdf",   no_argument,       NULL, OPT_ENABLE_HW_KDF },
 	{ "file-nonce",      required_argument, NULL, OPT_FILE_NONCE },
 	{ "fs-uuid",         required_argument, NULL, OPT_FS_UUID },
 	{ "help",            no_argument,       NULL, OPT_HELP },
@@ -1999,6 +2214,7 @@ static const struct option longopts[] = {
 	{ "kdf",             required_argument, NULL, OPT_KDF },
 	{ "mode-num",        required_argument, NULL, OPT_MODE_NUM },
 	{ "padding",         required_argument, NULL, OPT_PADDING },
+	{ "use-inlinecrypt-key", no_argument,   NULL, OPT_USE_INLINECRYPT_KEY },
 	{ NULL, 0, NULL, 0 },
 };
 
@@ -2025,6 +2241,7 @@ int main(int argc, char *argv[])
 	test_hkdf_sha512();
 	test_aes_256_xts();
 	test_aes_256_cts_cbc();
+	test_aes_256_cmac();
 	test_adiantum();
 #endif
 
@@ -2051,6 +2268,9 @@ int main(int argc, char *argv[])
 		case OPT_DUMP_KEY_IDENTIFIER:
 			dump_key_identifier = true;
 			break;
+		case OPT_ENABLE_HW_KDF:
+			params.enable_hw_kdf = true;
+			break;
 		case OPT_FILE_NONCE:
 			if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE)
 			    != FILE_NONCE_SIZE)
@@ -2090,6 +2310,9 @@ int main(int argc, char *argv[])
 			    padding > INT_MAX)
 				die("Invalid padding amount: %s", optarg);
 			break;
+		case OPT_USE_INLINECRYPT_KEY:
+			params.use_inlinecrypt_key = true;
+			break;
 		default:
 			usage(stderr);
 			return 2;
@@ -2122,12 +2345,20 @@ int main(int argc, char *argv[])
 	if (params.master_key_size < 0)
 		die("Invalid master_key: %s", *argv);
 
+	/* Derive sw_secret from master_key, if needed. */
+	if (params.enable_hw_kdf) {
+		derive_sw_secret(params.master_key, params.master_key_size,
+				 params.sw_secret);
+		params.sw_secret_size = SW_SECRET_SIZE;
+	} else {
+		memcpy(params.sw_secret, params.master_key,
+		       params.master_key_size);
+		params.sw_secret_size = params.master_key_size;
+	}
+
 	if (dump_key_identifier) {
 		do_dump_key_identifier(&params);
 	} else {
-		if (params.master_key_size < cipher->keysize)
-			die("Master key is too short for cipher %s",
-			    cipher->name);
 		get_key_and_iv(&params, real_key, cipher->keysize, &iv);
 		crypt_loop(cipher, real_key, &iv, decrypting, block_size,
 			   padding,
-- 
2.35.1


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

* [RFC PATCH 7/8] common/encrypt: support hardware-wrapped key testing
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
                   ` (5 preceding siblings ...)
  2022-02-28  7:47 ` [RFC PATCH 6/8] fscrypt-crypt-util: add hardware KDF support Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  2022-02-28  7:47 ` [RFC PATCH 8/8] generic: verify ciphertext with hardware-wrapped keys Eric Biggers
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

To support testing the kernel's support for hardware-wrapped inline
encryption keys, add some new functions to common/encrypt:

  - _require_hw_wrapped_key_support(): Checks that a block device
    supports hardware-wrapped keys and that some related fscryptctl
    commands are present.

  - _require_scratch_inlinecrypt(): Checks that the filesystem accepts
    the inlinecrypt mount option.

  - _generate_raw_hw_key(): Generates a raw key of an appropriate size
    for importing as a hardware-wrapped key.

  - _add_hw_wrapped_key(): Imports and prepares a hardware-wrapped key,
    then adds it to a filesystem.

In addition, update _require_encryption_policy_support() and
_verify_ciphertext_for_encryption_policy() to support
FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY.

Note: some of this relies on being able to call new block device ioctls.
For now these are accessed through the fscryptctl program.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/config  |   1 +
 common/encrypt | 116 ++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 106 insertions(+), 11 deletions(-)

diff --git a/common/config b/common/config
index 479e50d1..85a7919c 100644
--- a/common/config
+++ b/common/config
@@ -228,6 +228,7 @@ export E2IMAGE_PROG="$(type -P e2image)"
 export BLKZONE_PROG="$(type -P blkzone)"
 export GZIP_PROG="$(type -P gzip)"
 export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
+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.
diff --git a/common/encrypt b/common/encrypt
index d8e2dba9..e91e05c4 100644
--- a/common/encrypt
+++ b/common/encrypt
@@ -58,22 +58,21 @@ _require_scratch_encryption()
 	# If required, check for support for the specific type of encryption
 	# policy required by the test.
 	if [ $# -ne 0 ]; then
-		_require_encryption_policy_support $SCRATCH_MNT "$@"
+		_require_scratch_encryption_policy_support "$@"
 	fi
 
 	_scratch_unmount
 }
 
-_require_encryption_policy_support()
+_require_scratch_encryption_policy_support()
 {
-	local mnt=$1
-	local dir=$mnt/tmpdir
+	local dir=$SCRATCH_MNT/tmpdir
 	local set_encpolicy_args=""
 	local policy_flags=0
 	local policy_version=1
 	local c
 
-	OPTIND=2
+	OPTIND=1
 	while getopts "c:n:f:v:" c; do
 		case $c in
 		c|n)
@@ -94,6 +93,12 @@ _require_encryption_policy_support()
 	done
 	set_encpolicy_args=${set_encpolicy_args# }
 
+	if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then
+		echo "Checking whether $SCRATCH_DEV supports hardware-wrapped keys" \
+			>> $seqres.full
+		_require_hw_wrapped_key_support $SCRATCH_DEV
+	fi
+
 	echo "Checking whether kernel supports encryption policy: $set_encpolicy_args" \
 		>> $seqres.full
 
@@ -117,8 +122,15 @@ _require_encryption_policy_support()
 		# Both the kernel and xfs_io support v2 encryption policies, and
 		# therefore also filesystem-level keys -- since that's the only
 		# way to provide keys for v2 policies.
-		local raw_key=$(_generate_raw_encryption_key)
-		local keyspec=$(_add_enckey $mnt "$raw_key" | awk '{print $NF}')
+		if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then
+			local raw_key=$(_generate_raw_hw_key)
+			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
 		_require_command "$KEYCTL_PROG" keyctl
 		_new_session_keyring
@@ -143,6 +155,47 @@ _require_encryption_policy_support()
 	rm -r $dir
 }
 
+# Require that the filesystem accepts the "inlinecrypt" mount option.
+#
+# Note: this doesn't check whether $SCRATCH_DEV has any specific inline
+# encryption capabilities.  For encryption policies that require specific
+# capabilities, support is detected later by
+# _require_scratch_encryption_policy_support() (provided that inlinecrypt has
+# been added to $MOUNT_OPTIONS by that point).
+_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
+
+	_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
+
+	if ! head -c $RAW_HW_KEY_SIZE /dev/urandom | \
+		"$FSCRYPTCTL_PROG" import_hw_wrapped_key "$dev" \
+		>/dev/null 2>$tmp.err
+	then
+		if grep -E -q \
+			"(Operation not supported)|(Inappropriate ioctl for device)" $tmp.err
+		then
+			_notrun "$dev doesn't support hardware-wrapped inline encryption keys"
+		fi
+		_fail "Unexpected error from fscryptctl import_hw_wrapped_key: $(< $tmp.err)"
+	fi
+}
+
 _scratch_mkfs_encrypted()
 {
 	case $FSTYP in
@@ -220,14 +273,24 @@ _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
+
+# Generate a raw key of the proper size to be imported as a hardware-wrapped
+# key.
+_generate_raw_hw_key()
+{
+	_generate_raw_encryption_key $RAW_HW_KEY_SIZE
+}
+
 # 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.
@@ -358,6 +421,21 @@ _add_enckey()
 	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
@@ -771,6 +849,7 @@ FSCRYPT_MODE_ADIANTUM=9
 FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32=0x10
+FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY=0x20
 
 FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR=1
 FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER=2
@@ -800,6 +879,7 @@ _fscrypt_mode_name_to_num()
 #	'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
+#	'hw_wrapped_key':	test the HW_WRAPPED_KEY policy flag
 #
 _verify_ciphertext_for_encryption_policy()
 {
@@ -832,6 +912,9 @@ _verify_ciphertext_for_encryption_policy()
 		iv_ino_lblk_32)
 			(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 ))
 			;;
+		hw_wrapped_key)
+			(( policy_flags |= FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY ))
+			;;
 		*)
 			_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
 			;;
@@ -855,6 +938,10 @@ _verify_ciphertext_for_encryption_policy()
 		elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 )); then
 			crypt_util_args+=" --iv-ino-lblk-32"
 		fi
+		if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then
+			crypt_util_args+=" --enable-hw-kdf"
+			crypt_util_contents_args+=" --use-inlinecrypt-key"
+		fi
 	else
 		if (( policy_flags & ~FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then
 			_fail "unsupported flags for v1 policy: $policy_flags"
@@ -891,11 +978,18 @@ _verify_ciphertext_for_encryption_policy()
 	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 (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then
+			local raw_key=$(_generate_raw_hw_key)
+			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)
 		_new_session_keyring
 		_add_session_encryption_key $keyspec $raw_key
-- 
2.35.1


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

* [RFC PATCH 8/8] generic: verify ciphertext with hardware-wrapped keys
  2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
                   ` (6 preceding siblings ...)
  2022-02-28  7:47 ` [RFC PATCH 7/8] common/encrypt: support hardware-wrapped key testing Eric Biggers
@ 2022-02-28  7:47 ` Eric Biggers
  7 siblings, 0 replies; 9+ messages in thread
From: Eric Biggers @ 2022-02-28  7:47 UTC (permalink / raw)
  To: fstests; +Cc: linux-block, linux-fscrypt, Gaurav Kashyap

From: Eric Biggers <ebiggers@google.com>

Add two tests which verify that encrypted files are encrypted correctly
when a hardware-wrapped inline encryption key is used.  The two tests
are identical except that one uses FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64
and the other uses FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32.  These cover both
of the settings where hardware-wrapped keys currently may be used.

I've verified that these tests run and pass when all prerequisites are
met, namely:

- Hardware supporting the feature must be present.  I tested this on the
  SM8350 HDK (note: this currently requires a custom TrustZone image);
  this hardware is compatible with both of IV_INO_LBLK_{64,32}.
- The kernel patches for hardware-wrapped key support must be applied.
- The filesystem must be ext4 or f2fs.
- The kernel must have CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y.
- The fscryptctl program must be available, and must have patches for
  hardware-wrapped key support applied.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/900     | 30 ++++++++++++++++++++++++++++++
 tests/generic/900.out |  6 ++++++
 tests/generic/901     | 30 ++++++++++++++++++++++++++++++
 tests/generic/901.out |  6 ++++++
 4 files changed, 72 insertions(+)
 create mode 100755 tests/generic/900
 create mode 100644 tests/generic/900.out
 create mode 100755 tests/generic/901
 create mode 100644 tests/generic/901.out

diff --git a/tests/generic/900 b/tests/generic/900
new file mode 100755
index 00000000..a021732e
--- /dev/null
+++ b/tests/generic/900
@@ -0,0 +1,30 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+#
+# FS QA Test No. 900
+#
+# Verify the ciphertext for encryption policies that use the HW_WRAPPED_KEY and
+# IV_INO_LBLK_64 flags and that use AES-256-XTS to encrypt file contents and
+# AES-256-CTS-CBC to encrypt file names.
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+# Import common functions.
+. ./common/filter
+. ./common/encrypt
+
+# real QA test starts here
+_supported_fs generic
+
+# Hardware-wrapped keys require the inlinecrypt mount option.
+_require_scratch_inlinecrypt
+export MOUNT_OPTIONS="$MOUNT_OPTIONS -o inlinecrypt"
+
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC \
+	v2 iv_ino_lblk_64 hw_wrapped_key
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/900.out b/tests/generic/900.out
new file mode 100644
index 00000000..9edc012c
--- /dev/null
+++ b/tests/generic/900.out
@@ -0,0 +1,6 @@
+QA output created by 900
+
+Verifying ciphertext with parameters:
+	contents_encryption_mode: AES-256-XTS
+	filenames_encryption_mode: AES-256-CTS-CBC
+	options: v2 iv_ino_lblk_64 hw_wrapped_key
diff --git a/tests/generic/901 b/tests/generic/901
new file mode 100755
index 00000000..dd5c6e5f
--- /dev/null
+++ b/tests/generic/901
@@ -0,0 +1,30 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+#
+# FS QA Test No. 901
+#
+# Verify the ciphertext for encryption policies that use the HW_WRAPPED_KEY and
+# IV_INO_LBLK_32 flags and that use AES-256-XTS to encrypt file contents and
+# AES-256-CTS-CBC to encrypt file names.
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+# Import common functions.
+. ./common/filter
+. ./common/encrypt
+
+# real QA test starts here
+_supported_fs generic
+
+# Hardware-wrapped keys require the inlinecrypt mount option.
+_require_scratch_inlinecrypt
+export MOUNT_OPTIONS="$MOUNT_OPTIONS -o inlinecrypt"
+
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC \
+	v2 iv_ino_lblk_32 hw_wrapped_key
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/901.out b/tests/generic/901.out
new file mode 100644
index 00000000..2f928465
--- /dev/null
+++ b/tests/generic/901.out
@@ -0,0 +1,6 @@
+QA output created by 901
+
+Verifying ciphertext with parameters:
+	contents_encryption_mode: AES-256-XTS
+	filenames_encryption_mode: AES-256-CTS-CBC
+	options: v2 iv_ino_lblk_32 hw_wrapped_key
-- 
2.35.1


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

end of thread, other threads:[~2022-02-28  7:49 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-28  7:47 [RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 2/8] fscrypt-crypt-util: refactor get_key_and_iv() Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 3/8] fscrypt-crypt-util: add support for dumping key identifier Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 4/8] common/encrypt: log full ciphertext verification params Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 5/8] common/encrypt: verify the key identifiers Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 6/8] fscrypt-crypt-util: add hardware KDF support Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 7/8] common/encrypt: support hardware-wrapped key testing Eric Biggers
2022-02-28  7:47 ` [RFC PATCH 8/8] generic: verify ciphertext with hardware-wrapped keys Eric Biggers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.