From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Biggers Date: Tue, 02 Apr 2019 15:45:53 +0000 Subject: [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation Message-Id: <20190402154600.32432-11-ebiggers@kernel.org> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit List-Id: References: <20190402154600.32432-1-ebiggers@kernel.org> In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org> To: linux-fscrypt@vger.kernel.org Cc: Satya Tangirala , linux-api@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, keyrings@vger.kernel.org, linux-mtd@lists.infradead.org, linux-crypto@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, Paul Crowley From: Eric Biggers Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of deriving additional key material from the fscrypt master keys for v2 encryption policies. HKDF is a key derivation function built on top of HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an "hmac(sha512)" transform allocated from the crypto API. We'll be using this to replace the AES-ECB based KDF currently used to derive the per-file encryption keys. While the AES-ECB based KDF is believed to meet the original security requirements, it is nonstandard and has problems that don't exist in modern KDFs such as HKDF: 1. It's reversible. Given a derived key and nonce, an attacker can easily compute the master key. This is okay if the master key and derived keys are equally hard to compromise, but now we'd like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been removed. 2. It doesn't evenly distribute the entropy from the master key; each 16 input bytes only affects the corresponding 16 output bytes. 3. It isn't easily extensible to deriving other values or keys that are or will be needed, such as per-mode keys (which is what DIRECT_KEY policies really should use, rather than the master key directly as they do now) or a public hash for securely identifying the key (to ensure that an encrypted file cannot be set up with the wrong key). HKDF solves all the above problems. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/Makefile | 1 + fs/crypto/fscrypt_private.h | 15 +++ fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 fs/crypto/hkdf.c diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index f0de238000c02..c160598a9fe2c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,6 +7,8 @@ config FS_ENCRYPTION select CRYPTO_XTS select CRYPTO_CTS select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index accdd622c9083..4977b43479289 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o \ fname.o \ + hkdf.o \ hooks.o \ keyring.o \ keysetup.o \ diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1862c9baa1d..e2a65189eb578 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); +/* hkdf.c */ + +struct fscrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); + /* keyring.c */ /* diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c new file mode 100644 index 0000000000000..3f9dbf171d967 --- /dev/null +++ b/fs/crypto/hkdf.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the fscrypt master keys. + * + * Copyright 2019 Google LLC + */ + +#include +#include + +#include "fscrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses + * 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 pseudorandom key of length HKDF_HASHLEN bytes 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 pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to generate + * unnecessarily long keys. Thus fscrypt still does HKDF-Extract. + * + * HKDF-Extract also supports a salt. Choosing a random salt per input key + * would permit the input key to come from *any* source with sufficient entropy, + * even if it's not distributed uniformly. However, fscrypt doesn't take + * advantage of this because userspace should already provide good pseudorandom + * keys, which makes this unnecessary; also, having to persist a random salt per + * key from kernel mode would pose significant implementation complexity. Thus + * fscrypt uses a fixed salt. But to be slightly more robust against userspace + * (unwisely) reusing the fscrypt keys for another purpose, and to force + * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses + * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT) + +/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */ +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; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_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)) { + fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is + * thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + desc->flags = 0; + + 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; + } + + BUILD_BUG_ON(sizeof(context) != 1); + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + 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: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} -- 2.21.0 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Biggers Subject: [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation Date: Tue, 2 Apr 2019 08:45:53 -0700 Message-ID: <20190402154600.32432-11-ebiggers@kernel.org> References: <20190402154600.32432-1-ebiggers@kernel.org> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linux-f2fs-devel-bounces@lists.sourceforge.net To: linux-fscrypt@vger.kernel.org Cc: Satya Tangirala , linux-api@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, keyrings@vger.kernel.org, linux-mtd@lists.infradead.org, linux-crypto@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, Paul Crowley List-Id: linux-api@vger.kernel.org From: Eric Biggers Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of deriving additional key material from the fscrypt master keys for v2 encryption policies. HKDF is a key derivation function built on top of HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an "hmac(sha512)" transform allocated from the crypto API. We'll be using this to replace the AES-ECB based KDF currently used to derive the per-file encryption keys. While the AES-ECB based KDF is believed to meet the original security requirements, it is nonstandard and has problems that don't exist in modern KDFs such as HKDF: 1. It's reversible. Given a derived key and nonce, an attacker can easily compute the master key. This is okay if the master key and derived keys are equally hard to compromise, but now we'd like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been removed. 2. It doesn't evenly distribute the entropy from the master key; each 16 input bytes only affects the corresponding 16 output bytes. 3. It isn't easily extensible to deriving other values or keys that are or will be needed, such as per-mode keys (which is what DIRECT_KEY policies really should use, rather than the master key directly as they do now) or a public hash for securely identifying the key (to ensure that an encrypted file cannot be set up with the wrong key). HKDF solves all the above problems. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/Makefile | 1 + fs/crypto/fscrypt_private.h | 15 +++ fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 fs/crypto/hkdf.c diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index f0de238000c02..c160598a9fe2c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,6 +7,8 @@ config FS_ENCRYPTION select CRYPTO_XTS select CRYPTO_CTS select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index accdd622c9083..4977b43479289 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o \ fname.o \ + hkdf.o \ hooks.o \ keyring.o \ keysetup.o \ diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1862c9baa1d..e2a65189eb578 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); +/* hkdf.c */ + +struct fscrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); + /* keyring.c */ /* diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c new file mode 100644 index 0000000000000..3f9dbf171d967 --- /dev/null +++ b/fs/crypto/hkdf.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the fscrypt master keys. + * + * Copyright 2019 Google LLC + */ + +#include +#include + +#include "fscrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses + * 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 pseudorandom key of length HKDF_HASHLEN bytes 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 pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to generate + * unnecessarily long keys. Thus fscrypt still does HKDF-Extract. + * + * HKDF-Extract also supports a salt. Choosing a random salt per input key + * would permit the input key to come from *any* source with sufficient entropy, + * even if it's not distributed uniformly. However, fscrypt doesn't take + * advantage of this because userspace should already provide good pseudorandom + * keys, which makes this unnecessary; also, having to persist a random salt per + * key from kernel mode would pose significant implementation complexity. Thus + * fscrypt uses a fixed salt. But to be slightly more robust against userspace + * (unwisely) reusing the fscrypt keys for another purpose, and to force + * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses + * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT) + +/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */ +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; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_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)) { + fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is + * thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + desc->flags = 0; + + 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; + } + + BUILD_BUG_ON(sizeof(context) != 1); + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + 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: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} -- 2.21.0 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.1 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DC3E9C10F13 for ; Tue, 2 Apr 2019 15:49:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A361720645 for ; Tue, 2 Apr 2019 15:49:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1554220154; bh=rQjvkaYqfwA1ukak/+GujYTQLEQcY6Q9fCJS0mEsfLA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=Tm0CQJE0c0sM8mWvJnH4ARfy1MTtbKcRUwZS1wnmuNc0CAG4YI9htzS2HLCkI3F++ CoYMwrl5UJCiYqp0+TWD8WN5MQaHvFCGUBUS23pN1t81OsDu6mFf6BNTGq0hzBeoRD 0IxR+o3f22r86w2EGhr06mKA3ZKmrJvfBwFV8xuE= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731589AbfDBPtN (ORCPT ); Tue, 2 Apr 2019 11:49:13 -0400 Received: from mail.kernel.org ([198.145.29.99]:36918 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731229AbfDBPsg (ORCPT ); Tue, 2 Apr 2019 11:48:36 -0400 Received: from sol.localdomain (c-24-5-143-220.hsd1.ca.comcast.net [24.5.143.220]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 73EE7207E0; Tue, 2 Apr 2019 15:48:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1554220115; bh=rQjvkaYqfwA1ukak/+GujYTQLEQcY6Q9fCJS0mEsfLA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=1XMqMESpwfpIJF9Y7IiEAKCVuPLPlNf7/1odPzbSM+u2cMi/q8nb2GxBMEKj2sjCR UsnG5LcxwKmJg4jVuV3GS5zWaGkS8DD4k5I64rO9yM0xpgGdFI9b6hNIrl5u7YNhTy g7dLw7qbxEoRSr/IroQzgmpPgv1FVBxV6TchhwTc= From: Eric Biggers To: linux-fscrypt@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, Paul Crowley , Satya Tangirala Subject: [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation Date: Tue, 2 Apr 2019 08:45:53 -0700 Message-Id: <20190402154600.32432-11-ebiggers@kernel.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org> References: <20190402154600.32432-1-ebiggers@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-crypto-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org From: Eric Biggers Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of deriving additional key material from the fscrypt master keys for v2 encryption policies. HKDF is a key derivation function built on top of HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an "hmac(sha512)" transform allocated from the crypto API. We'll be using this to replace the AES-ECB based KDF currently used to derive the per-file encryption keys. While the AES-ECB based KDF is believed to meet the original security requirements, it is nonstandard and has problems that don't exist in modern KDFs such as HKDF: 1. It's reversible. Given a derived key and nonce, an attacker can easily compute the master key. This is okay if the master key and derived keys are equally hard to compromise, but now we'd like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been removed. 2. It doesn't evenly distribute the entropy from the master key; each 16 input bytes only affects the corresponding 16 output bytes. 3. It isn't easily extensible to deriving other values or keys that are or will be needed, such as per-mode keys (which is what DIRECT_KEY policies really should use, rather than the master key directly as they do now) or a public hash for securely identifying the key (to ensure that an encrypted file cannot be set up with the wrong key). HKDF solves all the above problems. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/Makefile | 1 + fs/crypto/fscrypt_private.h | 15 +++ fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 fs/crypto/hkdf.c diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index f0de238000c02..c160598a9fe2c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,6 +7,8 @@ config FS_ENCRYPTION select CRYPTO_XTS select CRYPTO_CTS select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index accdd622c9083..4977b43479289 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o \ fname.o \ + hkdf.o \ hooks.o \ keyring.o \ keysetup.o \ diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1862c9baa1d..e2a65189eb578 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); +/* hkdf.c */ + +struct fscrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); + /* keyring.c */ /* diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c new file mode 100644 index 0000000000000..3f9dbf171d967 --- /dev/null +++ b/fs/crypto/hkdf.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the fscrypt master keys. + * + * Copyright 2019 Google LLC + */ + +#include +#include + +#include "fscrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses + * 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 pseudorandom key of length HKDF_HASHLEN bytes 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 pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to generate + * unnecessarily long keys. Thus fscrypt still does HKDF-Extract. + * + * HKDF-Extract also supports a salt. Choosing a random salt per input key + * would permit the input key to come from *any* source with sufficient entropy, + * even if it's not distributed uniformly. However, fscrypt doesn't take + * advantage of this because userspace should already provide good pseudorandom + * keys, which makes this unnecessary; also, having to persist a random salt per + * key from kernel mode would pose significant implementation complexity. Thus + * fscrypt uses a fixed salt. But to be slightly more robust against userspace + * (unwisely) reusing the fscrypt keys for another purpose, and to force + * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses + * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT) + +/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */ +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; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_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)) { + fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is + * thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + desc->flags = 0; + + 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; + } + + BUILD_BUG_ON(sizeof(context) != 1); + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + 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: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} -- 2.21.0 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Eric Biggers Date: Tue, 2 Apr 2019 08:45:53 -0700 Message-Id: <20190402154600.32432-11-ebiggers@kernel.org> In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org> References: <20190402154600.32432-1-ebiggers@kernel.org> MIME-Version: 1.0 Subject: [f2fs-dev] [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: linux-f2fs-devel-bounces@lists.sourceforge.net To: linux-fscrypt@vger.kernel.org Cc: Satya Tangirala , linux-api@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, keyrings@vger.kernel.org, linux-mtd@lists.infradead.org, linux-crypto@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, Paul Crowley List-ID: From: Eric Biggers Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of deriving additional key material from the fscrypt master keys for v2 encryption policies. HKDF is a key derivation function built on top of HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an "hmac(sha512)" transform allocated from the crypto API. We'll be using this to replace the AES-ECB based KDF currently used to derive the per-file encryption keys. While the AES-ECB based KDF is believed to meet the original security requirements, it is nonstandard and has problems that don't exist in modern KDFs such as HKDF: 1. It's reversible. Given a derived key and nonce, an attacker can easily compute the master key. This is okay if the master key and derived keys are equally hard to compromise, but now we'd like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been removed. 2. It doesn't evenly distribute the entropy from the master key; each 16 input bytes only affects the corresponding 16 output bytes. 3. It isn't easily extensible to deriving other values or keys that are or will be needed, such as per-mode keys (which is what DIRECT_KEY policies really should use, rather than the master key directly as they do now) or a public hash for securely identifying the key (to ensure that an encrypted file cannot be set up with the wrong key). HKDF solves all the above problems. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/Makefile | 1 + fs/crypto/fscrypt_private.h | 15 +++ fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 fs/crypto/hkdf.c diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index f0de238000c02..c160598a9fe2c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,6 +7,8 @@ config FS_ENCRYPTION select CRYPTO_XTS select CRYPTO_CTS select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index accdd622c9083..4977b43479289 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o \ fname.o \ + hkdf.o \ hooks.o \ keyring.o \ keysetup.o \ diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1862c9baa1d..e2a65189eb578 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); +/* hkdf.c */ + +struct fscrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); + /* keyring.c */ /* diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c new file mode 100644 index 0000000000000..3f9dbf171d967 --- /dev/null +++ b/fs/crypto/hkdf.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the fscrypt master keys. + * + * Copyright 2019 Google LLC + */ + +#include +#include + +#include "fscrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses + * 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 pseudorandom key of length HKDF_HASHLEN bytes 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 pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to generate + * unnecessarily long keys. Thus fscrypt still does HKDF-Extract. + * + * HKDF-Extract also supports a salt. Choosing a random salt per input key + * would permit the input key to come from *any* source with sufficient entropy, + * even if it's not distributed uniformly. However, fscrypt doesn't take + * advantage of this because userspace should already provide good pseudorandom + * keys, which makes this unnecessary; also, having to persist a random salt per + * key from kernel mode would pose significant implementation complexity. Thus + * fscrypt uses a fixed salt. But to be slightly more robust against userspace + * (unwisely) reusing the fscrypt keys for another purpose, and to force + * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses + * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT) + +/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */ +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; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_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)) { + fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is + * thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + desc->flags = 0; + + 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; + } + + BUILD_BUG_ON(sizeof(context) != 1); + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + 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: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} -- 2.21.0 _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.0 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 42067C4360F for ; Tue, 2 Apr 2019 15:52:32 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0D13220645 for ; Tue, 2 Apr 2019 15:52:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="rUF6Fl8g"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=kernel.org header.i=@kernel.org header.b="1XMqMESp" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0D13220645 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=kernel.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-mtd-bounces+linux-mtd=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=tHj58f3ouf3EqlyawXWCs8Q1umjm+77nlaW6AnumOao=; b=rUF6Fl8gsbk9SO fkHd5dq+4FDq2ZVk9Cst6X0FxPYSrXFHrTTDqfmAMJ/JKOlxQsgTlvRh7jS62iOd9v27P3Zn8Cflb rU2atszIrClSL0R9fhArcynPBnsiXUUYL2BYK09evPDWZeFaMbYWh4EtXFIxUENtsafNrshGIFKaV v0gg1KeVXQ0xLYXcsoBOJhmkRVmXjmnX/ex2y+0CVJ+H5zqsUdkFPt5Teta7jbYjDoMNhLNt4RqmU 86QaVwTGeZ1D91r7pjhNSzWM/1VD6J+LVMukHTofmorGdIy45jjuEfSjZdOADl8GeuvXei5PmKYFb imwwrOFr6CbMd5qSi31g==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1hBLi8-0002VD-33; Tue, 02 Apr 2019 15:52:28 +0000 Received: from mail.kernel.org ([198.145.29.99]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1hBLeQ-0004nJ-C6 for linux-mtd@lists.infradead.org; Tue, 02 Apr 2019 15:48:58 +0000 Received: from sol.localdomain (c-24-5-143-220.hsd1.ca.comcast.net [24.5.143.220]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 73EE7207E0; Tue, 2 Apr 2019 15:48:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1554220115; bh=rQjvkaYqfwA1ukak/+GujYTQLEQcY6Q9fCJS0mEsfLA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=1XMqMESpwfpIJF9Y7IiEAKCVuPLPlNf7/1odPzbSM+u2cMi/q8nb2GxBMEKj2sjCR UsnG5LcxwKmJg4jVuV3GS5zWaGkS8DD4k5I64rO9yM0xpgGdFI9b6hNIrl5u7YNhTy g7dLw7qbxEoRSr/IroQzgmpPgv1FVBxV6TchhwTc= From: Eric Biggers To: linux-fscrypt@vger.kernel.org Subject: [PATCH v4 10/17] fscrypt: add an HKDF-SHA512 implementation Date: Tue, 2 Apr 2019 08:45:53 -0700 Message-Id: <20190402154600.32432-11-ebiggers@kernel.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190402154600.32432-1-ebiggers@kernel.org> References: <20190402154600.32432-1-ebiggers@kernel.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190402_084838_773047_1F4CF18D X-CRM114-Status: GOOD ( 23.82 ) X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Satya Tangirala , linux-api@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, keyrings@vger.kernel.org, linux-mtd@lists.infradead.org, linux-crypto@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, Paul Crowley Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-mtd" Errors-To: linux-mtd-bounces+linux-mtd=archiver.kernel.org@lists.infradead.org From: Eric Biggers Add an implementation of HKDF (RFC 5869) to fscrypt, for the purpose of deriving additional key material from the fscrypt master keys for v2 encryption policies. HKDF is a key derivation function built on top of HMAC. We choose SHA-512 for the underlying unkeyed hash, and use an "hmac(sha512)" transform allocated from the crypto API. We'll be using this to replace the AES-ECB based KDF currently used to derive the per-file encryption keys. While the AES-ECB based KDF is believed to meet the original security requirements, it is nonstandard and has problems that don't exist in modern KDFs such as HKDF: 1. It's reversible. Given a derived key and nonce, an attacker can easily compute the master key. This is okay if the master key and derived keys are equally hard to compromise, but now we'd like to be more robust against threats such as a derived key being compromised through a timing attack, or a derived key for an in-use file being compromised after the master key has already been removed. 2. It doesn't evenly distribute the entropy from the master key; each 16 input bytes only affects the corresponding 16 output bytes. 3. It isn't easily extensible to deriving other values or keys that are or will be needed, such as per-mode keys (which is what DIRECT_KEY policies really should use, rather than the master key directly as they do now) or a public hash for securely identifying the key (to ensure that an encrypted file cannot be set up with the wrong key). HKDF solves all the above problems. Signed-off-by: Eric Biggers --- fs/crypto/Kconfig | 2 + fs/crypto/Makefile | 1 + fs/crypto/fscrypt_private.h | 15 +++ fs/crypto/hkdf.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 fs/crypto/hkdf.c diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index f0de238000c02..c160598a9fe2c 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -7,6 +7,8 @@ config FS_ENCRYPTION select CRYPTO_XTS select CRYPTO_CTS select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC select KEYS help Enable encryption of files and directories. This diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index accdd622c9083..4977b43479289 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o \ fname.o \ + hkdf.o \ hooks.o \ keyring.o \ keysetup.o \ diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index cc1862c9baa1d..e2a65189eb578 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -175,6 +175,21 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); +/* hkdf.c */ + +struct fscrypt_hkdf { + struct crypto_shash *hmac_tfm; +}; + +extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_size); + +extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen); + +extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); + /* keyring.c */ /* diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c new file mode 100644 index 0000000000000..3f9dbf171d967 --- /dev/null +++ b/fs/crypto/hkdf.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation + * Function"), aka RFC 5869. See also the original paper (Krawczyk 2010): + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme". + * + * This is used to derive keys from the fscrypt master keys. + * + * Copyright 2019 Google LLC + */ + +#include +#include + +#include "fscrypt_private.h" + +/* + * HKDF supports any unkeyed cryptographic hash algorithm, but fscrypt uses + * 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 pseudorandom key of length HKDF_HASHLEN bytes 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 pseudorandom key of + * length HKDF_HASHLEN bytes. However, cipher modes other than AES-256-XTS take + * shorter keys, and we don't want to force users of those modes to generate + * unnecessarily long keys. Thus fscrypt still does HKDF-Extract. + * + * HKDF-Extract also supports a salt. Choosing a random salt per input key + * would permit the input key to come from *any* source with sufficient entropy, + * even if it's not distributed uniformly. However, fscrypt doesn't take + * advantage of this because userspace should already provide good pseudorandom + * keys, which makes this unnecessary; also, having to persist a random salt per + * key from kernel mode would pose significant implementation complexity. Thus + * fscrypt uses a fixed salt. But to be slightly more robust against userspace + * (unwisely) reusing the fscrypt keys for another purpose, and to force + * brute-force attacks to target the fscrypt KDF specifically, fscrypt uses + * "fscrypt_hkdf_salt" rather than the default of all 0's defined by RFC 5869. + */ + +#define HKDF_SALT "fscrypt_hkdf_salt" +#define HKDF_SALTLEN CONST_STRLEN(HKDF_SALT) + +/* HKDF-Extract (RFC 5869 section 2.2), see explanation above */ +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; + + err = crypto_shash_setkey(hmac_tfm, HKDF_SALT, HKDF_SALTLEN); + if (err) + return err; + + desc->tfm = hmac_tfm; + desc->flags = 0; + err = crypto_shash_digest(desc, ikm, ikmlen, prk); + shash_desc_zero(desc); + return err; +} + +/* + * Compute HKDF-Extract using the given master key as the input keying material, + * and prepare an HMAC transform object keyed by the resulting pseudorandom key. + * + * Afterwards, the keyed HMAC transform object can be used for HKDF-Expand many + * times without having to recompute HKDF-Extract each time. + */ +int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, + unsigned int master_key_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)) { + fscrypt_warn(NULL, "error allocating " HKDF_HMAC_ALG ": %ld", + PTR_ERR(hmac_tfm)); + return PTR_ERR(hmac_tfm); + } + + BUG_ON(crypto_shash_digestsize(hmac_tfm) != sizeof(prk)); + + err = hkdf_extract(hmac_tfm, master_key, master_key_size, prk); + if (err) + goto err_free_tfm; + + err = crypto_shash_setkey(hmac_tfm, prk, sizeof(prk)); + if (err) + goto err_free_tfm; + + hkdf->hmac_tfm = hmac_tfm; + goto out; + +err_free_tfm: + crypto_free_shash(hmac_tfm); +out: + memzero_explicit(prk, sizeof(prk)); + return err; +} + +/* + * HKDF-Expand (RFC 5869 section 2.3). This expands the pseudorandom key, which + * was already keyed into 'hkdf->hmac_tfm' by fscrypt_init_hkdf(), into 'okmlen' + * bytes of output keying material parameterized by the application-specific + * 'info' of length 'infolen' bytes, prefixed with the 'context' byte. This is + * thread-safe and may be called by multiple threads in parallel. + * + * ('context' isn't part of the HKDF specification; it's just a prefix fscrypt + * adds to its application-specific info strings to guarantee that it doesn't + * accidentally repeat an info string when using HKDF for different purposes.) + */ +int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, + const u8 *info, unsigned int infolen, + u8 *okm, unsigned int okmlen) +{ + SHASH_DESC_ON_STACK(desc, hkdf->hmac_tfm); + unsigned int i; + int err; + const u8 *prev = NULL; + u8 counter = 1; + u8 tmp[HKDF_HASHLEN]; + + if (WARN_ON(okmlen > 255 * HKDF_HASHLEN)) + return -EINVAL; + + desc->tfm = hkdf->hmac_tfm; + desc->flags = 0; + + 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; + } + + BUILD_BUG_ON(sizeof(context) != 1); + err = crypto_shash_update(desc, &context, 1); + if (err) + goto out; + + err = crypto_shash_update(desc, info, infolen); + if (err) + goto out; + + BUILD_BUG_ON(sizeof(counter) != 1); + 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: + if (unlikely(err)) + memzero_explicit(okm, okmlen); /* so caller doesn't need to */ + shash_desc_zero(desc); + return err; +} + +void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf) +{ + crypto_free_shash(hkdf->hmac_tfm); +} -- 2.21.0 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/