Skip to content

Commit

Permalink
fscrypt: fix derivation of SipHash keys on big endian CPUs
Browse files Browse the repository at this point in the history
commit 2fc2b43 upstream.

Typically, the cryptographic APIs that fscrypt uses take keys as byte
arrays, which avoids endianness issues.  However, siphash_key_t is an
exception.  It is defined as 'u64 key[2];', i.e. the 128-bit key is
expected to be given directly as two 64-bit words in CPU endianness.

fscrypt_derive_dirhash_key() and fscrypt_setup_iv_ino_lblk_32_key()
forgot to take this into account.  Therefore, the SipHash keys used to
index encrypted+casefolded directories differ on big endian vs. little
endian platforms, as do the SipHash keys used to hash inode numbers for
IV_INO_LBLK_32-encrypted directories.  This makes such directories
non-portable between these platforms.

Fix this by always using the little endian order.  This is a breaking
change for big endian platforms, but this should be fine in practice
since these features (encrypt+casefold support, and the IV_INO_LBLK_32
flag) aren't known to actually be used on any big endian platforms yet.

Fixes: aa408f8 ("fscrypt: derive dirhash key for casefolded directories")
Fixes: e3b1078 ("fscrypt: add support for IV_INO_LBLK_32 policies")
Cc: <stable@vger.kernel.org> # v5.6+
Link: https://lore.kernel.org/r/20210605075033.54424-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
ebiggers authored and gregkh committed Jul 14, 2021
1 parent e99d888 commit 73fd86c
Showing 1 changed file with 32 additions and 8 deletions.
40 changes: 32 additions & 8 deletions fs/crypto/keysetup.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,40 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
return err;
}

/*
* Derive a SipHash key from the given fscrypt master key and the given
* application-specific information string.
*
* Note that the KDF produces a byte array, but the SipHash APIs expect the key
* as a pair of 64-bit words. Therefore, on big endian CPUs we have to do an
* endianness swap in order to get the same results as on little endian CPUs.
*/
static int fscrypt_derive_siphash_key(const struct fscrypt_master_key *mk,
u8 context, const u8 *info,
unsigned int infolen, siphash_key_t *key)
{
int err;

err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, context, info, infolen,
(u8 *)key, sizeof(*key));
if (err)
return err;

BUILD_BUG_ON(sizeof(*key) != 16);
BUILD_BUG_ON(ARRAY_SIZE(key->key) != 2);
le64_to_cpus(&key->key[0]);
le64_to_cpus(&key->key[1]);
return 0;
}

int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
const struct fscrypt_master_key *mk)
{
int err;

err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_DIRHASH_KEY,
ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
(u8 *)&ci->ci_dirhash_key,
sizeof(ci->ci_dirhash_key));
err = fscrypt_derive_siphash_key(mk, HKDF_CONTEXT_DIRHASH_KEY,
ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
&ci->ci_dirhash_key);
if (err)
return err;
ci->ci_dirhash_key_initialized = true;
Expand Down Expand Up @@ -253,10 +278,9 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
if (mk->mk_ino_hash_key_initialized)
goto unlock;

err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
HKDF_CONTEXT_INODE_HASH_KEY, NULL, 0,
(u8 *)&mk->mk_ino_hash_key,
sizeof(mk->mk_ino_hash_key));
err = fscrypt_derive_siphash_key(mk,
HKDF_CONTEXT_INODE_HASH_KEY,
NULL, 0, &mk->mk_ino_hash_key);
if (err)
goto unlock;
/* pairs with smp_load_acquire() above */
Expand Down

0 comments on commit 73fd86c

Please sign in to comment.