generic: verify ciphertext of IV_INO_LBLK_32 encryption policies

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

The IV_INO_LBLK_32 encryption policy flag modifies the IV generation and
key derivation to be optimized for use with inline encryption hardware
that only accepts 32-bit IVs.  It is similar to IV_INO_LBLK_64 (which is
tested by generic/592), but it uses a trick to get the IV down to 32
bits.  For more information, see kernel commit e3b1078bedd3 ("fscrypt:
add support for IV_INO_LBLK_32 policies").

This test required adding SipHash support to fscrypt-crypt-util.

Running this test requires a kernel containing the above commit, e.g.
the latest mainline (which will become v5.8 and later).  For ext4, it
also needs an e2fsprogs version that supports the stable_inodes feature,
e.g. the latest git master branch (which will become v1.46 and later).

Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers
2020-06-03 19:25:01 -07:00
committed by Eryu Guan
parent a1c25b75b4
commit 35fd4ce0b3
5 changed files with 171 additions and 18 deletions
+13 -5
View File
@@ -97,7 +97,8 @@ _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
if (( policy_flags & (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 |
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) )); then
_scratch_unmount
_scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
_scratch_mount
@@ -769,6 +770,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_KEY_SPEC_TYPE_DESCRIPTOR=1
FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER=2
@@ -797,6 +799,7 @@ _fscrypt_mode_name_to_num()
# '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
# 'iv_ino_lblk_32': test the IV_INO_LBLK_32 policy flag
#
_verify_ciphertext_for_encryption_policy()
{
@@ -826,6 +829,9 @@ _verify_ciphertext_for_encryption_policy()
iv_ino_lblk_64)
(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 ))
;;
iv_ino_lblk_32)
(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 ))
;;
*)
_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
;;
@@ -841,14 +847,15 @@ _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"
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
@@ -872,7 +879,8 @@ _verify_ciphertext_for_encryption_policy()
fi
echo "Creating encryption-capable filesystem" >> $seqres.full
if (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then
if (( policy_flags & (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 |
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) )); then
_scratch_mkfs_stable_inodes_encrypted &>> $seqres.full
else
_scratch_mkfs_encrypted &>> $seqres.full
+108 -13
View File
@@ -63,10 +63,14 @@ static void usage(FILE *fp)
" --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"
" Required for --iv-ino-lblk-32 and\n"
" --iv-ino-lblk-64; otherwise is unused.\n"
" --help Show this help\n"
" --inode-number=INUM The file's inode number. Only required for\n"
" --iv-ino-lblk-64.\n"
" --inode-number=INUM The file's inode number. Required for\n"
" --iv-ino-lblk-32 and --iv-ino-lblk-64;\n"
" otherwise is unused.\n"
" --iv-ino-lblk-32 Similar to --iv-ino-lblk-64, but selects the\n"
" 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"
@@ -143,6 +147,11 @@ static inline u32 ror32(u32 v, int n)
return (v >> n) | (v << (32 - n));
}
static inline u64 rol64(u64 v, int n)
{
return (v << n) | (v >> (64 - n));
}
static inline u64 ror64(u64 v, int n)
{
return (v >> n) | (v << (64 - n));
@@ -1579,6 +1588,50 @@ static void test_adiantum(void)
}
#endif /* ENABLE_ALG_TESTS */
/*----------------------------------------------------------------------------*
* SipHash-2-4 *
*----------------------------------------------------------------------------*/
/*
* Reference: "SipHash: a fast short-input PRF"
* https://cr.yp.to/siphash/siphash-20120918.pdf
*/
#define SIPROUND \
do { \
v0 += v1; v2 += v3; \
v1 = rol64(v1, 13); v3 = rol64(v3, 16); \
v1 ^= v0; v3 ^= v2; \
v0 = rol64(v0, 32); \
v2 += v1; v0 += v3; \
v1 = rol64(v1, 17); v3 = rol64(v3, 21); \
v1 ^= v2; v3 ^= v0; \
v2 = rol64(v2, 32); \
} while (0)
/* Compute the SipHash-2-4 of a 64-bit number when formatted as little endian */
static u64 siphash_1u64(const u64 key[2], u64 data)
{
u64 v0 = key[0] ^ 0x736f6d6570736575ULL;
u64 v1 = key[1] ^ 0x646f72616e646f6dULL;
u64 v2 = key[0] ^ 0x6c7967656e657261ULL;
u64 v3 = key[1] ^ 0x7465646279746573ULL;
u64 m[2] = {data, (u64)sizeof(data) << 56};
size_t i;
for (i = 0; i < ARRAY_SIZE(m); i++) {
v3 ^= m[i];
SIPROUND;
SIPROUND;
v0 ^= m[i];
}
v2 ^= 0xff;
for (i = 0; i < 4; i++)
SIPROUND;
return v0 ^ v1 ^ v2 ^ v3;
}
/*----------------------------------------------------------------------------*
* Main program *
*----------------------------------------------------------------------------*/
@@ -1723,15 +1776,39 @@ struct key_and_iv_params {
u8 file_nonce[FILE_NONCE_SIZE];
bool file_nonce_specified;
bool iv_ino_lblk_64;
bool iv_ino_lblk_32;
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_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
/* 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)
{
u8 info[9] = "fscrypt";
union {
u64 words[2];
u8 bytes[16];
} hash_key;
info[8] = HKDF_CONTEXT_INODE_HASH_KEY;
hkdf_sha512(params->master_key, params->master_key_size,
NULL, 0, info, sizeof(info),
hash_key.bytes, sizeof(hash_key));
hash_key.words[0] = get_unaligned_le64(&hash_key.bytes[0]);
hash_key.words[1] = get_unaligned_le64(&hash_key.bytes[8]);
return (u32)siphash_1u64(hash_key.words, params->inode_number);
}
/*
* Get the key and starting IV with which the encryption will actually be done.
@@ -1752,8 +1829,20 @@ 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");
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)
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);
}
switch (params->kdf) {
case KDF_NONE:
@@ -1776,23 +1865,24 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
break;
case KDF_HKDF_SHA512:
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->iv_ino_lblk_32) {
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;
put_unaligned_le32(hash_inode_number(params),
iv->bytes);
} 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_KEY;
info[infolen++] = HKDF_CONTEXT_PER_FILE_ENC_KEY;
memcpy(&info[infolen], params->file_nonce,
FILE_NONCE_SIZE);
infolen += FILE_NONCE_SIZE;
@@ -1817,6 +1907,7 @@ enum {
OPT_FS_UUID,
OPT_HELP,
OPT_INODE_NUMBER,
OPT_IV_INO_LBLK_32,
OPT_IV_INO_LBLK_64,
OPT_KDF,
OPT_MODE_NUM,
@@ -1830,6 +1921,7 @@ static const struct option longopts[] = {
{ "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-32", no_argument, NULL, OPT_IV_INO_LBLK_32 },
{ "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 },
@@ -1890,6 +1982,9 @@ int main(int argc, char *argv[])
case OPT_INODE_NUMBER:
params.inode_number = parse_inode_number(optarg);
break;
case OPT_IV_INO_LBLK_32:
params.iv_ino_lblk_32 = true;
break;
case OPT_IV_INO_LBLK_64:
params.iv_ino_lblk_64 = true;
break;
+43
View File
@@ -0,0 +1,43 @@
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright 2020 Google LLC
#
# FS QA Test No. 602
#
# Verify ciphertext for v2 encryption policies that use the IV_INO_LBLK_32 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_32
# success, all done
status=0
exit
+6
View File
@@ -0,0 +1,6 @@
QA output created by 602
Verifying ciphertext with parameters:
contents_encryption_mode: AES-256-XTS
filenames_encryption_mode: AES-256-CTS-CBC
options: v2 iv_ino_lblk_32
+1
View File
@@ -604,3 +604,4 @@
599 auto quick remount shutdown
600 auto quick quota
601 auto quick quota
602 auto quick encrypt