@@ -8,6 +8,8 @@ config FS_ENCRYPTION
select CRYPTO_CTS
select CRYPTO_CTR
select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select CRYPTO_HMAC
select KEYS
help
Enable encryption of files and directories. This
@@ -6,6 +6,11 @@
* This contains encryption key functions.
*
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
+ *
+ * HKDF support and key add/remove API added by Eric Biggers, 2017.
+ *
+ * The implementation and usage of HKDF should conform to RFC-5869 ("HMAC-based
+ * Extract-and-Expand Key Derivation Function").
*/
#include <keys/user-type.h>
@@ -14,10 +19,181 @@
#include <linux/scatterlist.h>
#include <linux/seq_file.h>
#include <crypto/aes.h>
+#include <crypto/hash.h>
#include <crypto/sha.h>
#include "fscrypt_private.h"
-static struct crypto_shash *essiv_hash_tfm;
+/*
+ * Any unkeyed cryptographic hash algorithm can be used with HKDF, but we use
+ * SHA-512 because it is reasonably secure and efficient; and since it produces
+ * a 64-byte digest, deriving an AES-256-XTS key preserves all 64 bytes of
+ * entropy from the master key and requires only one iteration of HKDF-Expand.
+ */
+#define HKDF_HMAC_ALG "hmac(sha512)"
+#define HKDF_HASHLEN SHA512_DIGEST_SIZE
+
+/*
+ * HKDF consists of two steps:
+ *
+ * 1. HKDF-Extract: extract a fixed-length pseudorandom key from the
+ * input keying material and optional salt.
+ * 2. HKDF-Expand: expand the pseudorandom key into output keying material of
+ * any length, parameterized by an application-specific info string.
+ *
+ * HKDF-Extract can be skipped if the input is already a good pseudorandom key
+ * that is at least as long as the hash. While the fscrypt master keys should
+ * already be good pseudorandom keys, when using encryption algorithms that use
+ * short keys (e.g. AES-128-CBC) we'd like to permit the master key to be
+ * shorter than HKDF_HASHLEN bytes. Thus, we still must do HKDF-Extract.
+ *
+ * Ideally, HKDF-Extract would be passed a random salt for each distinct input
+ * key. Details about the advantages of a random salt can be found in the HKDF
+ * paper (Krawczyk, 2010; "Cryptographic Extraction and Key Derivation: The HKDF
+ * Scheme"). However, we do not have the ability to store a salt on a
+ * per-master-key basis. Thus, we have to use a fixed salt. This is sufficient
+ * as long as the master keys are already pseudorandom and are long enough to
+ * make dictionary attacks infeasible. This should be the case if userspace
+ * used a cryptographically secure random number generator, e.g. /dev/urandom,
+ * to generate the master keys and it was initialized with sufficient entropy.
+ *
+ * For the fixed salt we use "fscrypt_hkdf_salt" rather than default of all 0's
+ * defined by RFC-5869. This is only to be slightly more robust against
+ * userspace (unwisely) reusing the master keys for different purposes.
+ * Logically, it's more likely that the keys would be passed to unsalted
+ * HKDF-SHA512 than specifically to "fscrypt_hkdf_salt"-salted HKDF-SHA512.
+ * Of course, a random salt would be better for this purpose.
+ */
+
+#define HKDF_SALT "fscrypt_hkdf_salt"
+#define HKDF_SALT_LEN (sizeof(HKDF_SALT) - 1)
+
+/*
+ * HKDF-Extract (RFC-5869 section 2.2). This extracts a pseudorandom key 'prk'
+ * from the input key material 'ikm' and a salt. See explanation above for why
+ * we use a fixed salt.
+ */
+static int hkdf_extract(struct crypto_shash *hmac_tfm,
+ const u8 *ikm, unsigned int ikmlen,
+ u8 prk[HKDF_HASHLEN])
+{
+ SHASH_DESC_ON_STACK(desc, hmac_tfm);
+ int err;
+
+ desc->tfm = hmac_tfm;
+ desc->flags = 0;
+
+ err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALT_LEN);
+ if (err)
+ goto out;
+
+ err = crypto_shash_digest(desc, ikm, ikmlen, prk);
+out:
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * HKDF-Expand (RFC-5869 section 2.3). This expands the pseudorandom key, which
+ * has already been keyed into 'hmac_tfm', into 'okmlen' bytes of output keying
+ * material, parameterized by the application-specific information string of
+ * 'info' prefixed with the 'context' byte. ('context' isn't part of the HKDF
+ * specification; it's just a prefix we add to our application-specific info
+ * strings to guarantee that we don't accidentally repeat an info string when
+ * using HKDF for different purposes.)
+ */
+static int hkdf_expand(struct crypto_shash *hmac_tfm, u8 context,
+ const u8 *info, unsigned int infolen,
+ u8 *okm, unsigned int okmlen)
+{
+ SHASH_DESC_ON_STACK(desc, hmac_tfm);
+ int err;
+ const u8 *prev = NULL;
+ unsigned int i;
+ u8 counter = 1;
+ u8 tmp[HKDF_HASHLEN];
+
+ desc->tfm = hmac_tfm;
+ desc->flags = 0;
+
+ if (unlikely(okmlen > 255 * HKDF_HASHLEN))
+ return -EINVAL;
+
+ for (i = 0; i < okmlen; i += HKDF_HASHLEN) {
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, HKDF_HASHLEN);
+ if (err)
+ goto out;
+ }
+
+ err = crypto_shash_update(desc, &context, 1);
+ if (err)
+ goto out;
+
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+
+ if (okmlen - i < HKDF_HASHLEN) {
+ err = crypto_shash_finup(desc, &counter, 1, tmp);
+ if (err)
+ goto out;
+ memcpy(&okm[i], tmp, okmlen - i);
+ memzero_explicit(tmp, sizeof(tmp));
+ } else {
+ err = crypto_shash_finup(desc, &counter, 1, &okm[i]);
+ if (err)
+ goto out;
+ }
+ counter++;
+ prev = &okm[i];
+ }
+ err = 0;
+out:
+ shash_desc_zero(desc);
+ return err;
+}
+
+/*
+ * Precompute HKDF-Extract using the master key as the input key material, then
+ * return an HMAC transform that is keyed using the resulting pseudorandom key.
+ * This can be used to derive further key material using HKDF-Expand.
+ */
+static struct crypto_shash *allocate_hmac_tfm(const u8 *master_key, u32 size)
+{
+ struct crypto_shash *hmac_tfm;
+ u8 prk[HKDF_HASHLEN];
+ int err;
+
+ hmac_tfm = crypto_alloc_shash(HKDF_HMAC_ALG, 0, 0);
+ if (IS_ERR(hmac_tfm)) {
+ pr_warn("fscrypt: error allocating " HKDF_HMAC_ALG ": %ld\n",
+ PTR_ERR(hmac_tfm));
+ goto out;
+ }
+
+ BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk));
+
+ err = hkdf_extract(hmac_tfm, master_key, size, prk);
+ if (err)
+ goto fail;
+
+ err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk));
+ if (err)
+ goto fail;
+out:
+ memzero_explicit(prk, sizeof(prk));
+ return hmac_tfm;
+
+fail:
+ crypto_free_shash(hmac_tfm);
+ hmac_tfm = ERR_PTR(err);
+ goto out;
+}
/*
* fscrypt_master_key_secret - secret key material of an in-use master key
@@ -914,6 +1090,8 @@ static void put_crypt_info(struct fscrypt_info *ci)
kmem_cache_free(fscrypt_info_cachep, ci);
}
+static struct crypto_shash *essiv_hash_tfm;
+
static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt)
{
struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm);