From: Eric Biggers <ebiggers@kernel.org>
To: fstests@vger.kernel.org
Cc: linux-fscrypt@vger.kernel.org, Satya Tangirala <satyat@google.com>
Subject: [PATCH v2 4/5] common/encrypt: support verifying ciphertext of IV_INO_LBLK_64 policies
Date: Mon, 2 Dec 2019 15:01:54 -0800 [thread overview]
Message-ID: <20191202230155.99071-5-ebiggers@kernel.org> (raw)
In-Reply-To: <20191202230155.99071-1-ebiggers@kernel.org>
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.393.g34dc348eaf-goog
next prev parent reply other threads:[~2019-12-02 23:02 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-02 23:01 [PATCH v2 0/5] xfstests: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
2019-12-02 23:01 ` [PATCH v2 1/5] fscrypt-crypt-util: create key_and_iv_params structure Eric Biggers
2019-12-02 23:01 ` [PATCH v2 2/5] fscrypt-crypt-util: add HKDF context constants Eric Biggers
2019-12-02 23:01 ` [PATCH v2 3/5] common/encrypt: create named variables for UAPI constants Eric Biggers
2019-12-02 23:01 ` Eric Biggers [this message]
2019-12-02 23:01 ` [PATCH v2 5/5] generic: verify ciphertext of IV_INO_LBLK_64 encryption policies Eric Biggers
2019-12-09 18:18 ` [PATCH v2 0/5] xfstests: " Eric Biggers
2020-01-03 16:46 ` Eric Biggers
2020-01-06 2:55 ` Eryu Guan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191202230155.99071-5-ebiggers@kernel.org \
--to=ebiggers@kernel.org \
--cc=fstests@vger.kernel.org \
--cc=linux-fscrypt@vger.kernel.org \
--cc=satyat@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.