From patchwork Mon Oct 23 21:40:52 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 10023225 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 79A18605A8 for ; Mon, 23 Oct 2017 21:43:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6A43228921 for ; Mon, 23 Oct 2017 21:43:49 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5EDD928938; Mon, 23 Oct 2017 21:43:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CCF0F28921 for ; Mon, 23 Oct 2017 21:43:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932316AbdJWVnb (ORCPT ); Mon, 23 Oct 2017 17:43:31 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:51254 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932209AbdJWVmg (ORCPT ); Mon, 23 Oct 2017 17:42:36 -0400 Received: by mail-io0-f194.google.com with SMTP id b186so21740071iof.8; Mon, 23 Oct 2017 14:42:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AdQYfKtecEkjrPqGJc7GmeYZ5R0tQ7+mjuXrtXhv3cE=; b=IDXC3K9+77tXXXofmuOcNbeAwtevVmewKwsRAiB89YsrMQnckXwAgh8h6yX0XHIjXv DvXdjAawMLKqBrBkyFJLTRWNO+DSung/lZx10XSgGWgiuEQq+Cmi04GeEjFUfoWJRP/a XahJ70l0iL8b1NCt4UoS/Wpvk8fiNEnk/ILec0PVv/2Sc1Q1zU0PWkyDNOpMb+FTQkCY xKfOjHS9sh03tX7bSelLE3s8MDQ1SCpUHVaUmCfWTWgVCg/OdkVLTI28up7+kWzI47ER NHcW7W+KfwtZ5BOvt6Owzf4JcSjn7vLM/wUvWm9NBdTlNNSRKoCbtv1isg647gsJBNt0 xaww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AdQYfKtecEkjrPqGJc7GmeYZ5R0tQ7+mjuXrtXhv3cE=; b=ge9Cy8CO/KEqKAZm3I8X/hn+gZl2TWAYQ5YTf+2skxI8h8WRkpgGAvb2zKfzMzX1dJ SEDprFEDgnjF5pF1/NfyRat5KR9fvvoD0/DE38hET2iDyG69DzXO8I4mZw1R2MWqodAb 6WoKkCFjpd4Q/shSL97hTgz7E6LS3zu6cjiRrdc/s6ZtoHhPj1UtjIS0wLitt7efKg5C YnOlUPKGX4fdoEPjOwUKfzKW+x7iejT+qxAgeDvWZqUXaSOypw5fji+FvQ9HRbgv4jH8 zK9pFcjqG5jXrbKBVmWa11rgWAVx/xcyXcbzY3RBmjwkqfGoTnN5EePGw5OYsF+ynaCR KLxg== X-Gm-Message-State: AMCzsaWWYwmfIgGuMTzcgnnYGYQy8n4rO+MbzJtY36TcBMk5jHdiWwu3 9w950FkfCj32FflCVuwMWHp/nL6P X-Google-Smtp-Source: ABhQp+TU54ewO3yKgl+t8uOEZ1bu2tAkcffEx2YRVffG1fWbzAl6jMAK2WFm/2JU3XlxAENl61l4tA== X-Received: by 10.107.132.167 with SMTP id o39mr19830735ioi.243.1508794954930; Mon, 23 Oct 2017 14:42:34 -0700 (PDT) Received: from ebiggers-linuxstation.kir.corp.google.com ([100.66.175.88]) by smtp.gmail.com with ESMTPSA id i63sm3558482ioi.68.2017.10.23.14.42.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Oct 2017 14:42:34 -0700 (PDT) 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, keyrings@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Gwendal Grignou , Ryo Hashimoto , Sarthak Kukreti , Nick Desaulniers , Michael Halcrow , Eric Biggers 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 Message-Id: <20171023214058.128121-20-ebiggers3@gmail.com> X-Mailer: git-send-email 2.15.0.rc0.271.g36b669edcc-goog In-Reply-To: <20171023214058.128121-1-ebiggers3@gmail.com> References: <20171023214058.128121-1-ebiggers3@gmail.com> Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Eric Biggers 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 --- 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);