linux-api.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers3@gmail.com>
To: linux-fscrypt@vger.kernel.org
Cc: Ryo Hashimoto <hashimoto@chromium.org>,
	Gwendal Grignou <gwendal@chromium.org>,
	"Theodore Y . Ts'o" <tytso@mit.edu>,
	Eric Biggers <ebiggers@google.com>,
	linux-api@vger.kernel.org,
	Nick Desaulniers <ndesaulniers@google.com>,
	linux-f2fs-devel@lists.sourceforge.net, keyrings@vger.kernel.org,
	linux-mtd@lists.infradead.org,
	Michael Halcrow <mhalcrow@google.com>,
	Sarthak Kukreti <sarthakkukreti@chromium.org>,
	linux-fsdevel@vger.kernel.org, Jaegeuk Kim <jaegeuk@kernel.org>,
	linux-ext4@vger.kernel.org
Subject: [RFC PATCH 19/25] fscrypt: use HKDF-SHA512 to derive the per-file keys for v2 policies
Date: Mon, 23 Oct 2017 14:40:52 -0700	[thread overview]
Message-ID: <20171023214058.128121-20-ebiggers3@gmail.com> (raw)
In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com>

From: Eric Biggers <ebiggers@google.com>

The AES-ECB-based method we're using to derive the per-inode encryption
keys is nonstandard and has a number of problems, such as being
trivially reversible.  Fix these problems for v2 encryption policies by
deriving the keys using HKDF-SHA512 instead.  The inode's nonce prefixed
with a context byte is used as the application-specific info string.

Supposedly, one of the reasons that HKDF wasn't used originally was
because of performance concerns.  However, we actually can derive on the
order of 1 million keys per second, so it's likely not a bottleneck in
practice.  Moreover, although HKDF-SHA512 can require a bit more actual
crypto work per key derivation than the old KDF, the real world
performance is actually just as good or even better than the old KDF.
This is because the old KDF has to allocate and key a new "ecb(aes)"
transform for every key derivation (since it's keyed with the nonce
rather than the master key), whereas with HKDF we simply use a cached,
pre-keyed "hmac(sha512)" transform.  And the old KDF often spends more
time allocating its crypto transform than doing actual crypto work.

Another benefit to switching to HKDF is that we no longer need to hold
the raw master key in memory, but rather only an HMAC transform keyed by
a pseudorandom key extracted from the master key.  Of course, for the
software HMAC implementation there is no security benefit, since
compromising the state of the HMAC transform is equivalent to
compromising the raw master key.  However, there could be a security
benefit if used with an HMAC implementation that holds the secret in
crypto accelerator hardware rather than in main memory.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 fs/crypto/fscrypt_private.h |  4 ++--
 fs/crypto/keyinfo.c         | 58 ++++++++++++++++++++++++++++++++++-----------
 2 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index dec85c4b14a8..6d420c9a85bd 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -49,7 +49,7 @@ struct fscrypt_context_v1 {
 
 	/*
 	 * A unique value used in combination with the master key to derive the
-	 * file's actual encryption key
+	 * file's actual encryption key, using the AES-ECB-based KDF.
 	 */
 	u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
 };
@@ -78,7 +78,7 @@ struct fscrypt_context_v2 {
 
 	/*
 	 * A unique value used in combination with the master key to derive the
-	 * file's actual encryption key
+	 * file's actual encryption key, using HKDF-SHA512.
 	 */
 	u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
 };
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index c9e7b2b262d2..ec181c4eca56 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -44,6 +44,7 @@
  * "key identifier", as that is stored in the clear.)
  */
 #define HKDF_CONTEXT_KEY_IDENTIFIER	1
+#define HKDF_CONTEXT_PER_FILE_KEY	2
 
 /*
  * HKDF consists of two steps:
@@ -213,10 +214,18 @@ static struct crypto_shash *allocate_hmac_tfm(const u8 *master_key, u32 size)
  */
 struct fscrypt_master_key_secret {
 
-	/* Size of the raw key in bytes */
+	/*
+	 * For v2 policy keys: an HMAC transform keyed by the pseudorandom key
+	 * generated by computing HKDF-Extract with the raw master key as the
+	 * input key material.  This is used to efficiently derive the per-inode
+	 * encryption keys using HKDF-Expand later.
+	 */
+	struct crypto_shash	*hmac_tfm;
+
+	/* Size of the raw key in bytes.  Set even if ->raw isn't set. */
 	u32			size;
 
-	/* The raw key */
+	/* For v1 policy keys: the raw key. */
 	u8			raw[FSCRYPT_MAX_KEY_SIZE];
 };
 
@@ -291,6 +300,7 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret)
 
 static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret)
 {
+	crypto_free_shash(secret->hmac_tfm);
 	memzero_explicit(secret, sizeof(*secret));
 }
 
@@ -571,14 +581,17 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
 		goto out_wipe_secret;
 
 	if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
-		struct crypto_shash *hmac_tfm;
 
-		hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size);
-		if (IS_ERR(hmac_tfm)) {
-			err = PTR_ERR(hmac_tfm);
+		secret.hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size);
+		if (IS_ERR(secret.hmac_tfm)) {
+			err = PTR_ERR(secret.hmac_tfm);
+			secret.hmac_tfm = NULL;
 			goto out_wipe_secret;
 		}
 
+		/* The raw key is no longer needed */
+		memzero_explicit(secret.raw, sizeof(secret.raw));
+
 		/*
 		 * Hash the master key to get the key identifier, then return it
 		 * to userspace.  Specifically, we derive the key identifier
@@ -589,10 +602,9 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
 		 * rather than two (HKDF-SHA512 and SHA512).  It *maybe* would
 		 * be okay, but cryptographically it would be bad practice.
 		 */
-		err = hkdf_expand(hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER,
+		err = hkdf_expand(secret.hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER,
 				  NULL, 0, arg.key_spec.identifier,
 				  FSCRYPT_KEY_IDENTIFIER_SIZE);
-		crypto_free_shash(hmac_tfm);
 		if (err)
 			goto out_wipe_secret;
 
@@ -871,8 +883,13 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
 }
 
 /*
- * Key derivation function.  This generates the derived key by encrypting the
- * master key with AES-128-ECB using the nonce as the AES key.
+ * Legacy key derivation function.  This generates the derived key by encrypting
+ * the master key with AES-128-ECB using the nonce as the AES key.  This
+ * provides a unique derived key with sufficient entropy for each inode.
+ * However, it's nonstandard, non-extensible, doesn't evenly distribute the
+ * entropy from the master key, and is trivially reversible: an attacker who
+ * compromises a derived key can "decrypt" it to get back to the master key,
+ * then derive any other key.  For all new code, use HKDF instead.
  *
  * The master key must be at least as long as the derived key.  If the master
  * key is longer, then only the first 'derived_keysize' bytes are used.
@@ -1078,8 +1095,21 @@ static int find_and_derive_key(const struct inode *inode,
 		goto out_release_key;
 	}
 
-	err = derive_key_aes(mk->mk_secret.raw, &ctx->v1,
-			     derived_key, derived_keysize);
+	/*
+	 * Derive the inode's encryption key, given the master key and the nonce
+	 * from the inode's fscrypt_context.  v1 policies used an AES-ECB-based
+	 * KDF (Key Derivation Function).  Newer policies use HKDF-SHA512, which
+	 * fixes a number of problems with the AES-ECB-based KDF.
+	 */
+	if (ctx->v1.version == FSCRYPT_CONTEXT_V1) {
+		err = derive_key_aes(mk->mk_secret.raw, &ctx->v1,
+				     derived_key, derived_keysize);
+	} else {
+		err = hkdf_expand(mk->mk_secret.hmac_tfm,
+				  HKDF_CONTEXT_PER_FILE_KEY,
+				  ctx->v2.nonce, sizeof(ctx->v2.nonce),
+				  derived_key, derived_keysize);
+	}
 	if (err)
 		goto out_release_key;
 
@@ -1275,8 +1305,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
 		goto out;
 
 	/*
-	 * This cannot be a stack buffer because it is passed to the scatterlist
-	 * crypto API as part of key derivation.
+	 * This cannot be a stack buffer because it may be passed to the
+	 * scatterlist crypto API during key derivation.
 	 */
 	res = -ENOMEM;
 	derived_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
-- 
2.15.0.rc0.271.g36b669edcc-goog


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

  parent reply	other threads:[~2017-10-23 21:40 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-23 21:40 [RFC PATCH 00/25] fscrypt: filesystem-level keyring and v2 policy support Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 01/25] fs, fscrypt: move uapi definitions to new header <linux/fscrypt.h> Eric Biggers
2017-10-27 18:01   ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 02/25] fscrypt: use FSCRYPT_ prefix for uapi constants Eric Biggers
2017-10-27 18:02   ` Michael Halcrow via Linux-f2fs-devel
2017-10-23 21:40 ` [RFC PATCH 04/25] fscrypt: refactor finding and deriving key Eric Biggers
2017-10-27 18:23   ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 05/25] fs: add ->s_master_keys to struct super_block Eric Biggers
     [not found]   ` <20171023214058.128121-6-ebiggers3-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-10-27 18:26     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 06/25] fscrypt: add FS_IOC_ADD_ENCRYPTION_KEY ioctl Eric Biggers
2017-10-27 20:14   ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 07/25] fs/inode.c: export inode_lru_list_del() Eric Biggers
     [not found]   ` <20171023214058.128121-8-ebiggers3-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-10-27 20:28     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 08/25] fs/inode.c: rename and export dispose_list() Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 09/25] fs/dcache.c: add shrink_dcache_inode() Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 10/25] fscrypt: add FS_IOC_REMOVE_ENCRYPTION_KEY ioctl Eric Biggers
     [not found] ` <20171023214058.128121-1-ebiggers3-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-10-23 21:40   ` [RFC PATCH 03/25] fscrypt: use FSCRYPT_* definitions, not FS_* Eric Biggers
     [not found]     ` <20171023214058.128121-4-ebiggers3-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-10-27 18:06       ` Michael Halcrow
2017-10-23 21:40   ` [RFC PATCH 11/25] fscrypt: add FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 12/25] ext4 crypto: wire up new ioctls for managing encryption keys Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 13/25] f2fs " Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 14/25] ubifs " Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 15/25] fscrypt: add UAPI definitions to get/set v2 encryption policies Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 16/25] fscrypt: implement basic handling of " Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 17/25] fscrypt: add an HKDF-SHA512 implementation Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 18/25] fscrypt: allow adding and removing keys for v2 encryption policies Eric Biggers
2017-10-23 21:40 ` Eric Biggers [this message]
2017-10-23 21:40 ` [RFC PATCH 20/25] fscrypt: allow unprivileged users to add/remove keys for v2 policies Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 21/25] fscrypt: require that key be added when setting a v2 encryption policy Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 22/25] ext4 crypto: wire up FS_IOC_GET_ENCRYPTION_POLICY_EX Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 23/25] f2fs " Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 24/25] ubifs " Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 25/25] fscrypt: document the new ioctls and policy version Eric Biggers

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=20171023214058.128121-20-ebiggers3@gmail.com \
    --to=ebiggers3@gmail.com \
    --cc=ebiggers@google.com \
    --cc=gwendal@chromium.org \
    --cc=hashimoto@chromium.org \
    --cc=jaegeuk@kernel.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=linux-fscrypt@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=mhalcrow@google.com \
    --cc=ndesaulniers@google.com \
    --cc=sarthakkukreti@chromium.org \
    --cc=tytso@mit.edu \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).