From patchwork Mon Feb 28 07:05:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762454 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D91CC4167D for ; Mon, 28 Feb 2022 07:06:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233529AbiB1HGi (ORCPT ); Mon, 28 Feb 2022 02:06:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60472 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233502AbiB1HGg (ORCPT ); Mon, 28 Feb 2022 02:06:36 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 56F9566211; Sun, 27 Feb 2022 23:05:57 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id D670E6101B; Mon, 28 Feb 2022 07:05:56 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0A6C6C340F2; Mon, 28 Feb 2022 07:05:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646031956; bh=a9w8OxGDPrZWrrdXUlHDZikjqGEvX6+nZH+B7Zrmdoc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RchH+BZ6WWsZwbC0S2X5hBZUnWsicxhmf1NmzUrG/YRRuiuM+SLB58X1Al07Zyrry skwBgvi7ii6xZcT203qS3iB3WcV7YnmtF3OHHs1UiwwW/+9E+iT6m3eLj8hmScfdQw DjyCearvxeuFa7Eenyr464LsLvidgOUVVequtqp2glDKgJdxmZvUei0OKxenCfDzaH vj2dr+rM5xT/ZUJuzNpw33XbOy8cQf4kgfgUWwNu77mgIND0LMvTBXJyfaIUFhkYbk wcSAOkSS1nRGEIvOZmvbIPDC79ko8tvQRlsIH3ZOW5f7kQF3XMtBNfuPyCEER3soVg WSgXQfDc6LW9Q== From: Eric Biggers To: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: linux-arm-msm@vger.kernel.org, linux-scsi@vger.kernel.org, kernel-team@android.com, Gaurav Kashyap , Israel Rukshin Subject: [PATCH v5 1/3] block: add basic hardware-wrapped key support Date: Sun, 27 Feb 2022 23:05:18 -0800 Message-Id: <20220228070520.74082-2-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228070520.74082-1-ebiggers@kernel.org> References: <20220228070520.74082-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-block@vger.kernel.org From: Eric Biggers To prevent keys from being compromised if an attacker acquires read access to kernel memory, some inline encryption hardware can accept keys which are wrapped by a per-boot hardware-internal key. This avoids needing to keep the plaintext keys in kernel memory, without restricting the number of keys that can be used. Such keys can be initially generated either by software (in which case they must be imported to hardware to be wrapped) or directly by the hardware. There is also a mechanism to derive a "software secret" for cryptographic tasks that can't be handled by inline encryption. To support this hardware, allow struct blk_crypto_key to represent a hardware-wrapped key as an alternative to a standard key, and make drivers set flags in struct blk_crypto_profile to indicate which types of keys they support. Also add the derive_sw_secret() low-level operation, which drivers supporting wrapped keys must implement. For more information, see the detailed documentation which this patch adds to Documentation/block/inline-encryption.rst. Signed-off-by: Eric Biggers --- Documentation/block/inline-encryption.rst | 221 +++++++++++++++++++++- block/blk-crypto-fallback.c | 5 +- block/blk-crypto-internal.h | 1 + block/blk-crypto-profile.c | 44 +++++ block/blk-crypto.c | 45 ++++- drivers/md/dm-table.c | 1 + drivers/mmc/host/cqhci-crypto.c | 2 + drivers/scsi/ufs/ufshcd-crypto.c | 1 + fs/crypto/inline_crypt.c | 4 +- include/linux/blk-crypto-profile.h | 26 +++ include/linux/blk-crypto.h | 70 ++++++- 11 files changed, 397 insertions(+), 23 deletions(-) diff --git a/Documentation/block/inline-encryption.rst b/Documentation/block/inline-encryption.rst index 4d151fbe20583..5d43ba36634a8 100644 --- a/Documentation/block/inline-encryption.rst +++ b/Documentation/block/inline-encryption.rst @@ -77,10 +77,10 @@ Basic design ============ We introduce ``struct blk_crypto_key`` to represent an inline encryption key and -how it will be used. This includes the actual bytes of the key; the size of the -key; the algorithm and data unit size the key will be used with; and the number -of bytes needed to represent the maximum data unit number the key will be used -with. +how it will be used. This includes the type of the key (standard or +hardware-wrapped); the actual bytes of the key; the size of the key; the +algorithm and data unit size the key will be used with; and the number of bytes +needed to represent the maximum data unit number the key will be used with. We introduce ``struct bio_crypt_ctx`` to represent an encryption context. It contains a data unit number and a pointer to a blk_crypto_key. We add pointers @@ -302,3 +302,216 @@ kernel will pretend that the device does not support hardware inline encryption When the crypto API fallback is enabled, this means that all bios with and encryption context will use the fallback, and IO will complete as usual. When the fallback is disabled, a bio with an encryption context will be failed. + +.. _hardware_wrapped_keys: + +Hardware-wrapped keys +===================== + +Motivation and threat model +--------------------------- + +Linux storage encryption (dm-crypt, fscrypt, eCryptfs, etc.) traditionally +relies on the raw encryption key(s) being present in kernel memory so that the +encryption can be performed. This traditionally isn't seen as a problem because +the key(s) won't be present during an offline attack, which is the main type of +attack that storage encryption is intended to protect from. + +However, there is an increasing desire to also protect users' data from other +types of attacks (to the extent possible), including: + +- Cold boot attacks, where an attacker with physical access to a system suddenly + powers it off, then immediately dumps the system memory to extract recently + in-use encryption keys, then uses these keys to decrypt user data on-disk. + +- Online attacks where the attacker is able to read kernel memory without fully + compromising the system, followed by an offline attack where any extracted + keys can be used to decrypt user data on-disk. An example of such an online + attack would be if the attacker is able to run some code on the system that + exploits a Meltdown-like vulnerability but is unable to escalate privileges. + +- Online attacks where the attacker fully compromises the system, but their data + exfiltration is significantly time-limited and/or bandwidth-limited, so in + order to completely exfiltrate the data they need to extract the encryption + keys to use in a later offline attack. + +Hardware-wrapped keys are a feature of inline encryption hardware that is +designed to protect users' data from the above attacks (to the extent possible), +without introducing limitations such as a maximum number of keys. + +Note that it is impossible to **fully** protect users' data from these attacks. +Even in the attacks where the attacker "just" gets read access to kernel memory, +they can still extract any user data that is present in memory, including +plaintext pagecache pages of encrypted files. The focus here is just on +protecting the encryption keys, as those instantly give access to **all** user +data in any following offline attack, rather than just some of it (where which +data is included in that "some" might not be controlled by the attacker). + +Solution overview +----------------- + +Inline encryption hardware typically has "keyslots" into which software can +program keys for the hardware to use; the contents of keyslots typically can't +be read back by software. As such, the above security goals could be achieved +if the kernel simply erased its copy of the key(s) after programming them into +keyslot(s) and thereafter only referred to them via keyslot number. + +However, that naive approach runs into the problem that it limits the number of +unlocked keys to the number of keyslots, which typically is a small number. In +cases where there is only one encryption key system-wide (e.g., a full-disk +encryption key), that can be tolerable. However, in general there can be many +logged-in users with many different keys, and/or many running applications with +application-specific encrypted storage areas. This is especially true if +file-based encryption (e.g. fscrypt) is being used. + +Thus, it is important for the kernel to still have a way to "remind" the +hardware about a key, without actually having the raw key itself. This would +ensure that the number of hardware keyslots only limits the number of active I/O +requests, not other things such as the number of logged-in users, the number of +running apps, or the number of encrypted storage areas that apps can create. + +Somewhat less importantly, it is also desirable that the raw keys are never +visible to software at all, even while being initially unlocked. This would +ensure that a read-only compromise of system memory will never allow a key to be +extracted to be used off-system, even if it occurs when a key is being unlocked. + +To solve all these problems, some vendors of inline encryption hardware have +made their hardware support *hardware-wrapped keys*. Hardware-wrapped keys +are encrypted keys that can only be unwrapped (decrypted) and used by hardware +-- either by the inline encryption hardware itself, or by a dedicated hardware +block that can directly provision keys to the inline encryption hardware. + +(We refer to them as "hardware-wrapped keys" rather than simply "wrapped keys" +to add some clarity in cases where there could be other types of wrapped keys, +such as in file-based encryption. Key wrapping is a commonly used technique.) + +The key which wraps (encrypts) hardware-wrapped keys is a hardware-internal key +that is never exposed to software; it is either a persistent key (a "long-term +wrapping key") or a per-boot key (an "ephemeral wrapping key"). The long-term +wrapped form of the key is what is initially unlocked, but it is discarded as +soon as it is converted into an ephemerally-wrapped key. In-use +hardware-wrapped keys are always ephemerally-wrapped, not long-term wrapped. + +As inline encryption hardware can only be used to encrypt/decrypt data on-disk, +the hardware also includes a level of indirection; it doesn't use the unwrapped +key directly for inline encryption, but rather derives both an inline encryption +key and a "software secret" from it. Software can use the "software secret" for +tasks that can't use the inline encryption hardware, such as filenames +encryption. The software secret is not protected from memory compromise. + +Key hierarchy +------------- + +Here is the key hierarchy for a hardware-wrapped key:: + + Hardware-wrapped key + | + | + + | + ----------------------------- + | | + Inline encryption key Software secret + +The components are: + +- *Hardware-wrapped key*: a key for the hardware's KDF (Key Derivation + Function), in ephemerally-wrapped form. The key wrapping algorithm is a + hardware implementation detail that doesn't impact kernel operation, but a + strong authenticated encryption algorithm such as AES-256-GCM is recommended. + +- *Hardware KDF*: a KDF (Key Derivation Function) which the hardware uses to + derive subkeys after unwrapping the wrapped key. The hardware's choice of KDF + doesn't impact kernel operation, but it does need to be known for testing + purposes, and it's also assumed to have at least a 256-bit security strength. + All known hardware uses the SP800-108 KDF in Counter Mode with AES-256-CMAC, + with a particular choice of labels and contexts; new hardware should use this + already-vetted KDF. + +- *Inline encryption key*: a derived key which the hardware directly provisions + to a keyslot of the inline encryption hardware, without exposing it to + software. In all known hardware, this will always be an AES-256-XTS key. + However, in principle other encryption algorithms could be supported too. + Hardware must derive distinct subkeys for each supported encryption algorithm. + +- *Software secret*: a derived key which the hardware returns to software so + that software can use it for cryptographic tasks that can't use inline + encryption. This value is cryptographically isolated from the inline + encryption key, i.e. knowing one doesn't reveal the other. (The KDF ensures + this.) Currently, the software secret is always 32 bytes and thus is suitable + for cryptographic applications that require up to a 256-bit security strength. + Some use cases (e.g. full-disk encryption) won't require the software secret. + +Example: in the case of fscrypt, the fscrypt master key (the key used to unlock +a particular set of encrypted directories) is made hardware-wrapped. The inline +encryption key is used as the file contents encryption key, while the software +secret (rather than the master key directly) is used to key fscrypt's KDF +(HKDF-SHA512) to derive other subkeys such as filenames encryption keys. + +Note that currently this design assumes a single inline encryption key per +hardware-wrapped key, without any further key derivation. Thus, in the case of +fscrypt, currently hardware-wrapped keys are only compatible with the "inline +encryption optimized" settings, which use one file contents encryption key per +encryption policy rather than one per file. This design could be extended to +make the hardware derive per-file keys using per-file nonces passed down the +storage stack, and in fact some hardware already supports this; future work is +planned to remove this limitation by adding the corresponding kernel support. + +Kernel support +-------------- + +The inline encryption support of the kernel's block layer ("blk-crypto") has +been extended to support hardware-wrapped keys as an alternative to standard +keys, when hardware support is available. This works in the following way: + +- A ``key_types_supported`` field is added to the crypto capabilities in + ``struct blk_crypto_profile``. This allows device drivers to declare that + they support standard keys, hardware-wrapped keys, or both. + +- ``struct blk_crypto_key`` can now contain a hardware-wrapped key as an + alternative to a standard key; a ``key_type`` field is added to + ``struct blk_crypto_config`` to distinguish between the different key types. + This allows users of blk-crypto to en/decrypt data using a hardware-wrapped + key in a way very similar to using a standard key. + +- A new method ``blk_crypto_ll_ops::derive_sw_secret`` is added. Device drivers + that support hardware-wrapped keys must implement this method. Users of + blk-crypto can call ``blk_crypto_derive_sw_secret()`` to access this method. + +- The programming and eviction of hardware-wrapped keys happens via + ``blk_crypto_ll_ops::keyslot_program`` and + ``blk_crypto_ll_ops::keyslot_evict``, just like it does for standard keys. If + a driver supports hardware-wrapped keys, then it must handle hardware-wrapped + keys being passed to these methods. + +blk-crypto-fallback doesn't support hardware-wrapped keys. Therefore, +hardware-wrapped keys can only be used with actual inline encryption hardware. + +Currently, the kernel only works with hardware-wrapped keys in +ephemerally-wrapped form. No generic kernel interfaces are provided for +generating or importing hardware-wrapped keys in the first place, or converting +them to ephemerally-wrapped form. In Android, SoC vendors are required to +support these operations in their KeyMint implementation (a hardware abstraction +layer in userspace); for details, see the `Android documentation +`_. + +Testability +----------- + +Both the hardware KDF and the inline encryption itself are well-defined +algorithms that don't depend on any secrets other than the unwrapped key. +Therefore, if the unwrapped key is known to software, these algorithms can be +reproduced in software in order to verify the ciphertext that is written to disk +by the inline encryption hardware. + +However, the unwrapped key will only be known to software for testing if the +"import" functionality is used. Proper testing is not possible in the +"generate" case where the hardware generates the key itself. The correct +operation of the "generate" mode thus relies on the security and correctness of +the hardware RNG and its use to generate the key, as well as the testing of the +"import" mode as that should cover all parts other than the key generation. + +For an example of a test that verifies the ciphertext written to disk in the +"import" mode, see the "generic/900" and "generic/901" tests in xfstests, or +`Android's vts_kernel_encryption_test +`_. diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c index c87aba8584c64..3bd1f02ddef6b 100644 --- a/block/blk-crypto-fallback.c +++ b/block/blk-crypto-fallback.c @@ -87,7 +87,7 @@ static struct bio_set crypto_bio_split; * This is the key we set when evicting a keyslot. This *should* be the all 0's * key, but AES-XTS rejects that key, so we use some random bytes instead. */ -static u8 blank_key[BLK_CRYPTO_MAX_KEY_SIZE]; +static u8 blank_key[BLK_CRYPTO_MAX_STANDARD_KEY_SIZE]; static void blk_crypto_fallback_evict_keyslot(unsigned int slot) { @@ -539,7 +539,7 @@ static int blk_crypto_fallback_init(void) if (blk_crypto_fallback_inited) return 0; - prandom_bytes(blank_key, BLK_CRYPTO_MAX_KEY_SIZE); + prandom_bytes(blank_key, BLK_CRYPTO_MAX_STANDARD_KEY_SIZE); err = bioset_init(&crypto_bio_split, 64, 0, 0); if (err) @@ -552,6 +552,7 @@ static int blk_crypto_fallback_init(void) profile->ll_ops = blk_crypto_fallback_ll_ops; profile->max_dun_bytes_supported = BLK_CRYPTO_MAX_IV_SIZE; + profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_STANDARD; /* All blk-crypto modes have a crypto API fallback. */ for (i = 0; i < BLK_ENCRYPTION_MODE_MAX; i++) diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h index 2fb0d65a464ca..d36ae71afed3f 100644 --- a/block/blk-crypto-internal.h +++ b/block/blk-crypto-internal.h @@ -13,6 +13,7 @@ struct blk_crypto_mode { const char *cipher_str; /* crypto API name (for fallback case) */ unsigned int keysize; /* key size in bytes */ + unsigned int security_strength; /* security strength in bytes */ unsigned int ivsize; /* iv size in bytes */ }; diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c index 96c511967386d..8699d24e6a0d3 100644 --- a/block/blk-crypto-profile.c +++ b/block/blk-crypto-profile.c @@ -350,6 +350,8 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile, return false; if (profile->max_dun_bytes_supported < cfg->dun_bytes) return false; + if (!(profile->key_types_supported & cfg->key_type)) + return false; return true; } @@ -463,6 +465,42 @@ bool blk_crypto_register(struct blk_crypto_profile *profile, } EXPORT_SYMBOL_GPL(blk_crypto_register); +/** + * blk_crypto_derive_sw_secret() - Derive software secret from hardware-wrapped + * key + * @profile: the crypto profile of the device the key will be used on + * @wrapped_key: the hardware-wrapped key + * @wrapped_key_size: size of @wrapped_key in bytes + * @sw_secret: (output) the software secret + * + * Given a hardware-wrapped key, ask the hardware to derive the secret which + * software can use for cryptographic tasks other than inline encryption. This + * secret is guaranteed to be cryptographically isolated from the inline + * encryption key, i.e. derived with a different KDF context. + * + * Return: 0 on success, -EOPNOTSUPP if the given @profile doesn't support + * hardware-wrapped keys (or is NULL), -EBADMSG if the key isn't a valid + * hardware-wrapped key, or another -errno code. + */ +int blk_crypto_derive_sw_secret(struct blk_crypto_profile *profile, + const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + int err = -EOPNOTSUPP; + + if (profile && + (profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) && + profile->ll_ops.derive_sw_secret) { + blk_crypto_hw_enter(profile); + err = profile->ll_ops.derive_sw_secret(profile, wrapped_key, + wrapped_key_size, + sw_secret); + blk_crypto_hw_exit(profile); + } + return err; +} + /** * blk_crypto_intersect_capabilities() - restrict supported crypto capabilities * by child device @@ -486,10 +524,12 @@ void blk_crypto_intersect_capabilities(struct blk_crypto_profile *parent, child->max_dun_bytes_supported); for (i = 0; i < ARRAY_SIZE(child->modes_supported); i++) parent->modes_supported[i] &= child->modes_supported[i]; + parent->key_types_supported &= child->key_types_supported; } else { parent->max_dun_bytes_supported = 0; memset(parent->modes_supported, 0, sizeof(parent->modes_supported)); + parent->key_types_supported = 0; } } EXPORT_SYMBOL_GPL(blk_crypto_intersect_capabilities); @@ -522,6 +562,9 @@ bool blk_crypto_has_capabilities(const struct blk_crypto_profile *target, target->max_dun_bytes_supported) return false; + if (reference->key_types_supported & ~target->key_types_supported) + return false; + return true; } EXPORT_SYMBOL_GPL(blk_crypto_has_capabilities); @@ -556,5 +599,6 @@ void blk_crypto_update_capabilities(struct blk_crypto_profile *dst, sizeof(dst->modes_supported)); dst->max_dun_bytes_supported = src->max_dun_bytes_supported; + dst->key_types_supported = src->key_types_supported; } EXPORT_SYMBOL_GPL(blk_crypto_update_capabilities); diff --git a/block/blk-crypto.c b/block/blk-crypto.c index ec9efeeeca918..ebd9cf12f6547 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -21,16 +21,19 @@ const struct blk_crypto_mode blk_crypto_modes[] = { [BLK_ENCRYPTION_MODE_AES_256_XTS] = { .cipher_str = "xts(aes)", .keysize = 64, + .security_strength = 32, .ivsize = 16, }, [BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV] = { .cipher_str = "essiv(cbc(aes),sha256)", .keysize = 16, + .security_strength = 16, .ivsize = 16, }, [BLK_ENCRYPTION_MODE_ADIANTUM] = { .cipher_str = "adiantum(xchacha12,aes)", .keysize = 32, + .security_strength = 32, .ivsize = 32, }, }; @@ -68,7 +71,10 @@ static int __init bio_crypt_ctx_init(void) /* Sanity check that no algorithm exceeds the defined limits. */ for (i = 0; i < BLK_ENCRYPTION_MODE_MAX; i++) { - BUG_ON(blk_crypto_modes[i].keysize > BLK_CRYPTO_MAX_KEY_SIZE); + BUG_ON(blk_crypto_modes[i].keysize > + BLK_CRYPTO_MAX_STANDARD_KEY_SIZE); + BUG_ON(blk_crypto_modes[i].security_strength > + blk_crypto_modes[i].keysize); BUG_ON(blk_crypto_modes[i].ivsize > BLK_CRYPTO_MAX_IV_SIZE); } @@ -308,8 +314,9 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio, /** * blk_crypto_init_key() - Prepare a key for use with blk-crypto * @blk_key: Pointer to the blk_crypto_key to initialize. - * @raw_key: Pointer to the raw key. Must be the correct length for the chosen - * @crypto_mode; see blk_crypto_modes[]. + * @raw_key: the raw bytes of the key + * @raw_key_size: size of the raw key in bytes + * @key_type: type of the key -- either standard or hardware-wrapped * @crypto_mode: identifier for the encryption algorithm to use * @dun_bytes: number of bytes that will be used to specify the DUN when this * key is used @@ -318,7 +325,9 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio, * Return: 0 on success, -errno on failure. The caller is responsible for * zeroizing both blk_key and raw_key when done with them. */ -int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key, +int blk_crypto_init_key(struct blk_crypto_key *blk_key, + const u8 *raw_key, unsigned int raw_key_size, + enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, unsigned int data_unit_size) @@ -331,8 +340,19 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key, return -EINVAL; mode = &blk_crypto_modes[crypto_mode]; - if (mode->keysize == 0) + switch (key_type) { + case BLK_CRYPTO_KEY_TYPE_STANDARD: + if (raw_key_size != mode->keysize) + return -EINVAL; + break; + case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED: + if (raw_key_size < mode->security_strength || + raw_key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) + return -EINVAL; + break; + default: return -EINVAL; + } if (dun_bytes == 0 || dun_bytes > mode->ivsize) return -EINVAL; @@ -343,9 +363,10 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key, blk_key->crypto_cfg.crypto_mode = crypto_mode; blk_key->crypto_cfg.dun_bytes = dun_bytes; blk_key->crypto_cfg.data_unit_size = data_unit_size; + blk_key->crypto_cfg.key_type = key_type; blk_key->data_unit_size_bits = ilog2(data_unit_size); - blk_key->size = mode->keysize; - memcpy(blk_key->raw, raw_key, mode->keysize); + blk_key->size = raw_key_size; + memcpy(blk_key->raw, raw_key, raw_key_size); return 0; } @@ -358,8 +379,10 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key, bool blk_crypto_config_supported(struct request_queue *q, const struct blk_crypto_config *cfg) { - return IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) || - __blk_crypto_cfg_supported(q->crypto_profile, cfg); + if (IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) && + cfg->key_type == BLK_CRYPTO_KEY_TYPE_STANDARD) + return true; + return __blk_crypto_cfg_supported(q->crypto_profile, cfg); } /** @@ -382,6 +405,10 @@ int blk_crypto_start_using_key(const struct blk_crypto_key *key, { if (__blk_crypto_cfg_supported(q->crypto_profile, &key->crypto_cfg)) return 0; + if (key->crypto_cfg.key_type != BLK_CRYPTO_KEY_TYPE_STANDARD) { + pr_warn_once("tried to use wrapped key, but hardware doesn't support it\n"); + return -EOPNOTSUPP; + } return blk_crypto_fallback_start_using_mode(key->crypto_cfg.crypto_mode); } diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index e43096cfe9e22..502a36398b1aa 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1297,6 +1297,7 @@ static int dm_table_construct_crypto_profile(struct dm_table *t) profile->max_dun_bytes_supported = UINT_MAX; memset(profile->modes_supported, 0xFF, sizeof(profile->modes_supported)); + profile->key_types_supported = ~0; for (i = 0; i < dm_table_get_num_targets(t); i++) { ti = dm_table_get_target(t, i); diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c index d5f4b6972f63e..6652982410ec5 100644 --- a/drivers/mmc/host/cqhci-crypto.c +++ b/drivers/mmc/host/cqhci-crypto.c @@ -210,6 +210,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host) /* Unfortunately, CQHCI crypto only supports 32 DUN bits. */ profile->max_dun_bytes_supported = 4; + profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_STANDARD; + /* * Cache all the crypto capabilities and advertise the supported crypto * modes and data unit sizes to the block layer. diff --git a/drivers/scsi/ufs/ufshcd-crypto.c b/drivers/scsi/ufs/ufshcd-crypto.c index 67402baf6faee..0ed82741f9810 100644 --- a/drivers/scsi/ufs/ufshcd-crypto.c +++ b/drivers/scsi/ufs/ufshcd-crypto.c @@ -190,6 +190,7 @@ int ufshcd_hba_init_crypto_capabilities(struct ufs_hba *hba) hba->crypto_profile.ll_ops = ufshcd_crypto_ops; /* UFS only supports 8 bytes for any DUN */ hba->crypto_profile.max_dun_bytes_supported = 8; + hba->crypto_profile.key_types_supported = BLK_CRYPTO_KEY_TYPE_STANDARD; hba->crypto_profile.dev = hba->dev; /* diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index c57bebfa48fea..ecb0cda469880 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -105,6 +105,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; crypto_cfg.data_unit_size = sb->s_blocksize; crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); + crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_STANDARD; num_devs = fscrypt_get_num_devices(sb); devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); if (!devs) @@ -143,7 +144,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, blk_key->num_devs = num_devs; fscrypt_get_devices(sb, num_devs, blk_key->devs); - err = blk_crypto_init_key(&blk_key->base, raw_key, crypto_mode, + err = blk_crypto_init_key(&blk_key->base, raw_key, ci->ci_mode->keysize, + BLK_CRYPTO_KEY_TYPE_STANDARD, crypto_mode, fscrypt_get_dun_bytes(ci), sb->s_blocksize); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); diff --git a/include/linux/blk-crypto-profile.h b/include/linux/blk-crypto-profile.h index bbab65bd54288..706de8c91ec9e 100644 --- a/include/linux/blk-crypto-profile.h +++ b/include/linux/blk-crypto-profile.h @@ -57,6 +57,21 @@ struct blk_crypto_ll_ops { int (*keyslot_evict)(struct blk_crypto_profile *profile, const struct blk_crypto_key *key, unsigned int slot); + + /** + * @derive_sw_secret: Derive the software secret from a hardware-wrapped + * key. + * + * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED + * is supported. + * + * Must return 0 on success, -EBADMSG if the key is invalid, or another + * -errno code on other errors. + */ + int (*derive_sw_secret)(struct blk_crypto_profile *profile, + const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); }; /** @@ -84,6 +99,12 @@ struct blk_crypto_profile { */ unsigned int max_dun_bytes_supported; + /** + * @key_types_supported: Supported types of keys -- + * BLK_CRYPTO_KEY_TYPE_STANDARD and/or BLK_CRYPTO_KEY_TYPE_HW_WRAPPED. + */ + unsigned int key_types_supported; + /** * @modes_supported: Array of bitmasks that specifies whether each * combination of crypto mode and data unit size is supported. @@ -154,6 +175,11 @@ void blk_crypto_reprogram_all_keys(struct blk_crypto_profile *profile); void blk_crypto_profile_destroy(struct blk_crypto_profile *profile); +int blk_crypto_derive_sw_secret(struct blk_crypto_profile *profile, + const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); + void blk_crypto_intersect_capabilities(struct blk_crypto_profile *parent, const struct blk_crypto_profile *child); diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index 69b24fe92cbf1..3a0b834869207 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h @@ -16,7 +16,58 @@ enum blk_crypto_mode_num { BLK_ENCRYPTION_MODE_MAX, }; -#define BLK_CRYPTO_MAX_KEY_SIZE 64 +/* + * Supported types of keys. Must be bit-flags due to their use in + * blk_crypto_profile::key_types_supported. + */ +enum blk_crypto_key_type { + /* + * Standard keys (i.e. "software keys"). These keys are simply kept in + * raw, plaintext form in kernel memory. + */ + BLK_CRYPTO_KEY_TYPE_STANDARD = 1 << 0, + + /* + * Hardware-wrapped keys. These keys are only present in kernel memory + * in ephemerally-wrapped form, and they can only be unwrapped by + * dedicated hardware. For details, see the "Hardware-wrapped keys" + * section of Documentation/block/inline-encryption.rst. + */ + BLK_CRYPTO_KEY_TYPE_HW_WRAPPED = 1 << 1, +}; + +/* + * Currently the maximum standard key size is 64 bytes, as that is the key size + * of BLK_ENCRYPTION_MODE_AES_256_XTS which takes the longest key. + * + * The maximum hardware-wrapped key size depends on the hardware's key wrapping + * algorithm, which is a hardware implementation detail, so it isn't precisely + * specified. But currently 128 bytes is plenty in practice. Implementations + * are recommended to wrap a 32-byte key for the hardware KDF with AES-256-GCM, + * which should result in a size closer to 64 bytes than 128. + * + * Both of these values can trivially be increased if ever needed. + */ +#define BLK_CRYPTO_MAX_STANDARD_KEY_SIZE 64 +#define BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE 128 + +/* This should use max(), but max() doesn't work in a struct definition. */ +#define BLK_CRYPTO_MAX_ANY_KEY_SIZE \ + (BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE > \ + BLK_CRYPTO_MAX_STANDARD_KEY_SIZE ? \ + BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE : BLK_CRYPTO_MAX_STANDARD_KEY_SIZE) + +/* + * Size of the "software secret" which can be derived from a hardware-wrapped + * key. This is currently always 32 bytes. Note, the choice of 32 bytes + * assumes that the software secret is only used directly for algorithms that + * don't require more than a 256-bit key to get the desired security strength. + * If it were to be used e.g. directly as an AES-256-XTS key, then this would + * need to be increased (which is possible if hardware supports it, but care + * would need to be taken to avoid breaking users who need exactly 32 bytes). + */ +#define BLK_CRYPTO_SW_SECRET_SIZE 32 + /** * struct blk_crypto_config - an inline encryption key's crypto configuration * @crypto_mode: encryption algorithm this key is for @@ -25,20 +76,23 @@ enum blk_crypto_mode_num { * ciphertext. This is always a power of 2. It might be e.g. the * filesystem block size or the disk sector size. * @dun_bytes: the maximum number of bytes of DUN used when using this key + * @key_type: the type of this key -- either standard or hardware-wrapped */ struct blk_crypto_config { enum blk_crypto_mode_num crypto_mode; unsigned int data_unit_size; unsigned int dun_bytes; + enum blk_crypto_key_type key_type; }; /** * struct blk_crypto_key - an inline encryption key - * @crypto_cfg: the crypto configuration (like crypto_mode, key size) for this - * key + * @crypto_cfg: the crypto mode, data unit size, key type, and other + * characteristics of this key and how it will be used * @data_unit_size_bits: log2 of data_unit_size - * @size: size of this key in bytes (determined by @crypto_cfg.crypto_mode) - * @raw: the raw bytes of this key. Only the first @size bytes are used. + * @size: size of this key in bytes. The size of a standard key is fixed for a + * given crypto mode, but the size of a hardware-wrapped key can vary. + * @raw: the bytes of this key. Only the first @size bytes are significant. * * A blk_crypto_key is immutable once created, and many bios can reference it at * the same time. It must not be freed until all bios using it have completed @@ -48,7 +102,7 @@ struct blk_crypto_key { struct blk_crypto_config crypto_cfg; unsigned int data_unit_size_bits; unsigned int size; - u8 raw[BLK_CRYPTO_MAX_KEY_SIZE]; + u8 raw[BLK_CRYPTO_MAX_ANY_KEY_SIZE]; }; #define BLK_CRYPTO_MAX_IV_SIZE 32 @@ -89,7 +143,9 @@ bool bio_crypt_dun_is_contiguous(const struct bio_crypt_ctx *bc, unsigned int bytes, const u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]); -int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key, +int blk_crypto_init_key(struct blk_crypto_key *blk_key, + const u8 *raw_key, unsigned int raw_key_size, + enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, unsigned int data_unit_size); From patchwork Mon Feb 28 07:05:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762452 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7889C43217 for ; Mon, 28 Feb 2022 07:06:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233523AbiB1HGh (ORCPT ); Mon, 28 Feb 2022 02:06:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60470 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233490AbiB1HGg (ORCPT ); Mon, 28 Feb 2022 02:06:36 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A59C2673D5; Sun, 27 Feb 2022 23:05:57 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 32B2160FEB; Mon, 28 Feb 2022 07:05:57 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 66373C340E7; Mon, 28 Feb 2022 07:05:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646031956; bh=p4LZmBEhwI9Ubw6ivvY3meI1rm5OqUixAiwCXXYTlBA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pUsOpFF0O9XuxhFfwhuvrO4TjuR86d8Ba1arBp1gRw+uhUlGOfLiQF23RX+BJcA1s yEei6c2KCn2DgJGhby4rPuR43NMby4KK7+VNDKUpVd+SdyvGPoZzvjINGa2VA35yl0 Fvmja7ombJM0xz1qr+XKDSrOlOb48QtXDdehVXMw6TS8RQyVJ+Y/MHJBLcIn4yB3Yz y19Uz9T7lDQrs5O0qPBGFMQNZh6lwqwUaYRpqD3rQM/bXhRg2JaP8gRndgqfCAdc7l gnI6dWRtsDufOBeBFky3sfbciwLf8eZHBvSQ2adkB9yCCKXvayltEt3/0Penqu+nqp 2lvbeOntPh3zg== From: Eric Biggers To: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: linux-arm-msm@vger.kernel.org, linux-scsi@vger.kernel.org, kernel-team@android.com, Gaurav Kashyap , Israel Rukshin Subject: [PATCH v5 2/3] block: add ioctls to create and prepare hardware-wrapped keys Date: Sun, 27 Feb 2022 23:05:19 -0800 Message-Id: <20220228070520.74082-3-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228070520.74082-1-ebiggers@kernel.org> References: <20220228070520.74082-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-block@vger.kernel.org From: Eric Biggers Until this point, the kernel can use hardware-wrapped keys to do encryption if userspace provides one -- specifically a key in ephemerally-wrapped form. However, no generic way has been provided for userspace to get such a key in the first place. Getting such a key is a two-step process. First, the key needs to be imported from a raw key or generated by the hardware, producing a key in long-term wrapped form. This happens once in the whole lifetime of the key. Second, the long-term wrapped key needs to be converted into ephemerally-wrapped form. This happens each time the key is "unlocked". In Android, these operations are supported in a generic way through KeyMint, a userspace abstraction layer. However, that method is Android-specific and can't be used on other Linux systems, may rely on proprietary libraries, and also misleads people into supporting KeyMint features like rollback resistance that make sense for other KeyMint keys but don't make sense for hardware-wrapped inline encryption keys. Therefore, this patch provides a generic kernel interface for these operations by introducing new block device ioctls: - BLKCRYPTOIMPORTKEY: convert a raw key to long-term wrapped form. - BLKCRYPTOGENERATEKEY: have the hardware generate a new key, then return it in long-term wrapped form. - BLKCRYPTOPREPAREKEY: convert a key from long-term wrapped form to ephemerally-wrapped form. These ioctls are implemented using new operations in blk_crypto_ll_ops. In combination with the rest of the hardware-wrapped keys framework, these ioctls allow the hardware-wrapped key support of inline encryption hardware to be used end-to-end on any Linux system. Signed-off-by: Eric Biggers --- Documentation/block/inline-encryption.rst | 34 +++-- block/blk-crypto-internal.h | 9 ++ block/blk-crypto-profile.c | 53 ++++++++ block/blk-crypto.c | 145 ++++++++++++++++++++++ block/ioctl.c | 5 + include/linux/blk-crypto-profile.h | 54 ++++++++ include/uapi/linux/fs.h | 26 ++++ 7 files changed, 319 insertions(+), 7 deletions(-) diff --git a/Documentation/block/inline-encryption.rst b/Documentation/block/inline-encryption.rst index 5d43ba36634a8..871603a15f749 100644 --- a/Documentation/block/inline-encryption.rst +++ b/Documentation/block/inline-encryption.rst @@ -487,13 +487,33 @@ keys, when hardware support is available. This works in the following way: blk-crypto-fallback doesn't support hardware-wrapped keys. Therefore, hardware-wrapped keys can only be used with actual inline encryption hardware. -Currently, the kernel only works with hardware-wrapped keys in -ephemerally-wrapped form. No generic kernel interfaces are provided for -generating or importing hardware-wrapped keys in the first place, or converting -them to ephemerally-wrapped form. In Android, SoC vendors are required to -support these operations in their KeyMint implementation (a hardware abstraction -layer in userspace); for details, see the `Android documentation -`_. +All the above deals with hardware-wrapped keys in ephemerally-wrapped form only. +To get such keys in the first place, new block device ioctls have also been +added to provide a generic interface to creating and preparing such keys: + +- ``BLKCRYPTOIMPORTKEY`` converts a raw key to long-term wrapped form. It takes + in a pointer to a ``struct blk_crypto_import_key_arg``. The caller must set + ``raw_key_ptr`` and ``raw_key_size`` to the pointer and size (in bytes) of the + raw key to import. The ioctl will write the resulting long-term wrapped key + to the buffer pointed to by ``longterm_wrapped_key_ptr``, which is of maximum + size ``longterm_wrapped_key_size``. It will also update + ``longterm_wrapped_key_size`` to be the actual size of the key. The ioctl + will return 0 on success, or will return -1 and set errno on failure. + +- ``BLKCRYPTOGENERATEKEY`` is like ``BLKCRYPTOIMPORTKEY``, but it has the + hardware generate the key instead of importing one. It takes in a pointer to + a ``struct blk_crypto_generate_key_arg``. + +- ``BLKCRYPTOPREPAREKEY`` converts a key from long-term wrapped form to + ephemerally-wrapped form. It takes in a pointer to a + ``struct blk_crypto_prepare_key_arg``. The caller must set + ``longterm_wrapped_key_ptr`` and ``longterm_wrapped_key_size`` to the pointer + and size (in bytes) of the long-term wrapped key to convert. The ioctl will + write the resulting ephemerally-wrapped key to the buffer pointed to by + ``ephemerally_wrapped_key_ptr``, which is of maximum size + ``ephemerally_wrapped_key_size``. It will also update + ``ephemerally_wrapped_key_size`` to be the actual size of the key. The ioctl + will return 0 on success, or will return -1 and set errno on failure. Testability ----------- diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h index d36ae71afed3f..28993a72a2d69 100644 --- a/block/blk-crypto-internal.h +++ b/block/blk-crypto-internal.h @@ -61,6 +61,9 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq) return rq->crypt_ctx; } +int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd, + void __user *argp); + #else /* CONFIG_BLK_INLINE_ENCRYPTION */ static inline bool bio_crypt_rq_ctx_compatible(struct request *rq, @@ -94,6 +97,12 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq) return false; } +static inline int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd, + void __user *argp) +{ + return -ENOTTY; +} + #endif /* CONFIG_BLK_INLINE_ENCRYPTION */ void __bio_crypt_advance(struct bio *bio, unsigned int bytes); diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c index 8699d24e6a0d3..5ea989845f055 100644 --- a/block/blk-crypto-profile.c +++ b/block/blk-crypto-profile.c @@ -501,6 +501,59 @@ int blk_crypto_derive_sw_secret(struct blk_crypto_profile *profile, return err; } +int blk_crypto_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + int ret = -EOPNOTSUPP; + + if (profile && + (profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) && + profile->ll_ops.import_key) { + blk_crypto_hw_enter(profile); + ret = profile->ll_ops.import_key(profile, raw_key, raw_key_size, + longterm_wrapped_key); + blk_crypto_hw_exit(profile); + } + return ret; +} + +int blk_crypto_generate_key(struct blk_crypto_profile *profile, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + int ret = -EOPNOTSUPP; + + if (profile && + (profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) && + profile->ll_ops.generate_key) { + blk_crypto_hw_enter(profile); + ret = profile->ll_ops.generate_key(profile, + longterm_wrapped_key); + blk_crypto_hw_exit(profile); + } + return ret; +} + +int blk_crypto_prepare_key(struct blk_crypto_profile *profile, + const u8 *longterm_wrapped_key, + size_t longterm_wrapped_key_size, + u8 ephemerally_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + int ret = -EOPNOTSUPP; + + if (profile && + (profile->key_types_supported & BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) && + profile->ll_ops.prepare_key) { + blk_crypto_hw_enter(profile); + ret = profile->ll_ops.prepare_key(profile, + longterm_wrapped_key, + longterm_wrapped_key_size, + ephemerally_wrapped_key); + blk_crypto_hw_exit(profile); + } + return ret; +} + /** * blk_crypto_intersect_capabilities() - restrict supported crypto capabilities * by child device diff --git a/block/blk-crypto.c b/block/blk-crypto.c index ebd9cf12f6547..1acf103727e61 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -438,3 +438,148 @@ int blk_crypto_evict_key(struct request_queue *q, return blk_crypto_fallback_evict_key(key); } EXPORT_SYMBOL_GPL(blk_crypto_evict_key); + +static int blk_crypto_ioctl_import_key(struct blk_crypto_profile *profile, + void __user *argp) +{ + struct blk_crypto_import_key_arg arg; + u8 raw_key[BLK_CRYPTO_MAX_STANDARD_KEY_SIZE]; + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]; + int ret; + + if (copy_from_user(&arg, argp, sizeof(arg))) + return -EFAULT; + + if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved))) + return -EINVAL; + + if (arg.raw_key_size < 16 || arg.raw_key_size > sizeof(raw_key)) + return -EINVAL; + + if (copy_from_user(raw_key, u64_to_user_ptr(arg.raw_key_ptr), + arg.raw_key_size)) { + ret = -EFAULT; + goto out; + } + ret = blk_crypto_import_key(profile, raw_key, arg.raw_key_size, + longterm_wrapped_key); + if (ret < 0) + goto out; + if (ret > arg.longterm_wrapped_key_size) { + ret = -ENOBUFS; + goto out; + } + arg.longterm_wrapped_key_size = ret; + if (copy_to_user(u64_to_user_ptr(arg.longterm_wrapped_key_ptr), + longterm_wrapped_key, arg.longterm_wrapped_key_size) || + copy_to_user(argp, &arg, sizeof(arg))) { + ret = -EFAULT; + goto out; + } + ret = 0; +out: + memzero_explicit(raw_key, sizeof(raw_key)); + memzero_explicit(longterm_wrapped_key, sizeof(longterm_wrapped_key)); + return ret; +} + +static int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile, + void __user *argp) +{ + struct blk_crypto_generate_key_arg arg; + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]; + int ret; + + if (copy_from_user(&arg, argp, sizeof(arg))) + return -EFAULT; + + if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved))) + return -EINVAL; + + ret = blk_crypto_generate_key(profile, longterm_wrapped_key); + if (ret < 0) + goto out; + if (ret > arg.longterm_wrapped_key_size) { + ret = -ENOBUFS; + goto out; + } + arg.longterm_wrapped_key_size = ret; + if (copy_to_user(u64_to_user_ptr(arg.longterm_wrapped_key_ptr), + longterm_wrapped_key, arg.longterm_wrapped_key_size) || + copy_to_user(argp, &arg, sizeof(arg))) { + ret = -EFAULT; + goto out; + } + ret = 0; +out: + memzero_explicit(longterm_wrapped_key, sizeof(longterm_wrapped_key)); + return ret; +} + +static int blk_crypto_ioctl_prepare_key(struct blk_crypto_profile *profile, + void __user *argp) +{ + struct blk_crypto_prepare_key_arg arg; + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]; + u8 ephemerally_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]; + int ret; + + if (copy_from_user(&arg, argp, sizeof(arg))) + return -EFAULT; + + if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved))) + return -EINVAL; + + if (arg.longterm_wrapped_key_size > sizeof(longterm_wrapped_key)) + return -EINVAL; + if (copy_from_user(longterm_wrapped_key, + u64_to_user_ptr(arg.longterm_wrapped_key_ptr), + arg.longterm_wrapped_key_size)) { + ret = -EFAULT; + goto out; + } + ret = blk_crypto_prepare_key(profile, longterm_wrapped_key, + arg.longterm_wrapped_key_size, + ephemerally_wrapped_key); + if (ret < 0) + goto out; + if (ret > arg.ephemerally_wrapped_key_size) { + ret = -ENOBUFS; + goto out; + } + arg.ephemerally_wrapped_key_size = ret; + if (copy_to_user(u64_to_user_ptr(arg.ephemerally_wrapped_key_ptr), + ephemerally_wrapped_key, + arg.ephemerally_wrapped_key_size) || + copy_to_user(argp, &arg, sizeof(arg))) { + ret = -EFAULT; + goto out; + } + ret = 0; +out: + memzero_explicit(longterm_wrapped_key, sizeof(longterm_wrapped_key)); + memzero_explicit(ephemerally_wrapped_key, + sizeof(ephemerally_wrapped_key)); + return ret; +} + +int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd, + void __user *argp) +{ + struct blk_crypto_profile *profile = + bdev_get_queue(bdev)->crypto_profile; + + if (!profile) + return -EOPNOTSUPP; + + switch (cmd) { + case BLKCRYPTOIMPORTKEY: + return blk_crypto_ioctl_import_key(profile, argp); + case BLKCRYPTOGENERATEKEY: + return blk_crypto_ioctl_generate_key(profile, argp); + case BLKCRYPTOPREPAREKEY: + return blk_crypto_ioctl_prepare_key(profile, argp); + default: + return -ENOTTY; + } +} diff --git a/block/ioctl.c b/block/ioctl.c index 4a86340133e46..f934aea41647f 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -12,6 +12,7 @@ #include #include #include "blk.h" +#include "blk-crypto-internal.h" static int blkpg_do_ioctl(struct block_device *bdev, struct blkpg_partition __user *upart, int op) @@ -506,6 +507,10 @@ static int blkdev_common_ioctl(struct block_device *bdev, fmode_t mode, case BLKTRACESTOP: case BLKTRACETEARDOWN: return blk_trace_ioctl(bdev, cmd, argp); + case BLKCRYPTOIMPORTKEY: + case BLKCRYPTOGENERATEKEY: + case BLKCRYPTOPREPAREKEY: + return blk_crypto_ioctl(bdev, cmd, argp); case IOC_PR_REGISTER: return blkdev_pr_register(bdev, argp); case IOC_PR_RESERVE: diff --git a/include/linux/blk-crypto-profile.h b/include/linux/blk-crypto-profile.h index 706de8c91ec9e..f55bd22185fa7 100644 --- a/include/linux/blk-crypto-profile.h +++ b/include/linux/blk-crypto-profile.h @@ -72,6 +72,48 @@ struct blk_crypto_ll_ops { const u8 *wrapped_key, unsigned int wrapped_key_size, u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); + + /** + * @import_key: Create a hardware-wrapped key by importing a raw key. + * + * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED + * is supported. + * + * Must return the size (in bytes) of the resulting wrapped key on + * success, or -errno on failure. + */ + int (*import_key)(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); + + /** + * @generate_key: Generate a hardware-wrapped key. + * + * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED + * is supported. + * + * Must return the size (in bytes) of the resulting wrapped key on + * success, or -errno on failure. + */ + int (*generate_key)(struct blk_crypto_profile *profile, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); + + /** + * @prepare_key: Prepare a hardware-wrapped key to be used. + * + * This function prepares a hardware-wrapped to be used by converting it + * from long-term wrapped form to ephemerally-wrapped form. + * + * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED + * is supported. + * + * Must return the size (in bytes) of the resulting wrapped key on + * success, or -errno on failure. + */ + int (*prepare_key)(struct blk_crypto_profile *profile, + const u8 *longterm_wrapped_key, + size_t longterm_wrapped_key_size, + u8 ephemerally_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); }; /** @@ -180,6 +222,18 @@ int blk_crypto_derive_sw_secret(struct blk_crypto_profile *profile, unsigned int wrapped_key_size, u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); +int blk_crypto_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); + +int blk_crypto_generate_key(struct blk_crypto_profile *profile, + u8 longterm_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); + +int blk_crypto_prepare_key(struct blk_crypto_profile *profile, + const u8 *longterm_wrapped_key, + size_t longterm_wrapped_key_size, + u8 ephemerally_wrapped_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]); + void blk_crypto_intersect_capabilities(struct blk_crypto_profile *parent, const struct blk_crypto_profile *child); diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index bdf7b404b3e72..d5021094c2fee 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -121,6 +121,28 @@ struct fsxattr { unsigned char fsx_pad[8]; }; +struct blk_crypto_import_key_arg { + __u64 raw_key_ptr; + __u64 raw_key_size; + __u64 longterm_wrapped_key_ptr; + __u64 longterm_wrapped_key_size; + __u64 reserved[4]; +}; + +struct blk_crypto_generate_key_arg { + __u64 longterm_wrapped_key_ptr; + __u64 longterm_wrapped_key_size; + __u64 reserved[4]; +}; + +struct blk_crypto_prepare_key_arg { + __u64 longterm_wrapped_key_ptr; + __u64 longterm_wrapped_key_size; + __u64 ephemerally_wrapped_key_ptr; + __u64 ephemerally_wrapped_key_size; + __u64 reserved[4]; +}; + /* * Flags for the fsx_xflags field */ @@ -185,6 +207,10 @@ struct fsxattr { #define BLKROTATIONAL _IO(0x12,126) #define BLKZEROOUT _IO(0x12,127) #define BLKGETDISKSEQ _IOR(0x12,128,__u64) +#define BLKCRYPTOIMPORTKEY _IOWR(0x12, 129, struct blk_crypto_import_key_arg) +#define BLKCRYPTOGENERATEKEY _IOWR(0x12, 130, struct blk_crypto_generate_key_arg) +#define BLKCRYPTOPREPAREKEY _IOWR(0x12, 131, struct blk_crypto_prepare_key_arg) + /* * A jump here: 130-136 are reserved for zoned block devices * (see uapi/linux/blkzoned.h) From patchwork Mon Feb 28 07:05:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762455 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 85AD2C433EF for ; Mon, 28 Feb 2022 07:06:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233548AbiB1HGw (ORCPT ); Mon, 28 Feb 2022 02:06:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60502 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233536AbiB1HGk (ORCPT ); Mon, 28 Feb 2022 02:06:40 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 01D31673D5; Sun, 27 Feb 2022 23:06:00 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 4D4BCB80E47; Mon, 28 Feb 2022 07:05:58 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BBFAFC340F3; Mon, 28 Feb 2022 07:05:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646031957; bh=Y4stKXvtrRCZufs5FIUjBslZmScYk27fvJ+t5lxkN3I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fMg+AvETmBtQpq7GKYyu+pXkUBEomh2dJzT5bVZ/KaaVXF+S/pqtiKMm7hPa0jlER 2btMqOQtAc2x89v+ICDBvL5xmS6qoXGvLwULaXTlxy9xDP9+CAgpcjM+d9baOPhuHJ Ak7MQYbhtsZkVQWn9ZEu0jUjAVY4K8eH/I4LXLuJjlbyvs458FSC+I7xNVj8zZdMfl 3tWeCYfzzVKhi9DcfmEpwnFSb/EELHnxpaAXdUo5nEvwaLsAdNWuIddUFccEil4bK/ ZnSG0mFeBvhsNOGNxBLaIs2I/WAdCG+cDn5sGb7Wfe8XbcI2gQFjhGpx3lehrkaJqN TS9+C6qd2U3Yg== From: Eric Biggers To: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: linux-arm-msm@vger.kernel.org, linux-scsi@vger.kernel.org, kernel-team@android.com, Gaurav Kashyap , Israel Rukshin Subject: [PATCH v5 3/3] fscrypt: add support for hardware-wrapped keys Date: Sun, 27 Feb 2022 23:05:20 -0800 Message-Id: <20220228070520.74082-4-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228070520.74082-1-ebiggers@kernel.org> References: <20220228070520.74082-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-block@vger.kernel.org From: Eric Biggers Add support for hardware-wrapped keys to fscrypt. Hardware-wrapped keys are inline encryption keys which are only present in kernel memory in ephemerally-wrapped form, and which can only be unwrapped by dedicated hardware. Such keys are protected from certain attacks, such as cold boot attacks. For more information, see the "Hardware-wrapped keys" section of Documentation/block/inline-encryption.rst. To support hardware-wrapped keys in fscrypt, we allow the fscrypt master keys to be hardware-wrapped, and we allow encryption policies to be flagged as needing a hardware-wrapped key. File contents encryption is done by passing the wrapped key to the inline encryption hardware via blk-crypto. Other fscrypt operations such as filenames encryption continue to be done by the kernel, using the "software secret" which the hardware derives. For more information, see the documentation which this patch adds to Documentation/filesystems/fscrypt.rst. Note that this feature doesn't require any filesystem-specific changes. However it does depend on inline encryption support, and thus currently it is only applicable to ext4 and f2fs, not to ubifs or CephFS. This feature is intentionally not UAPI or on-disk format compatible with the version of this feature in the Android Common Kernels, as that version was meant as a temporary solution and it took some shortcuts. Once upstreamed, this new version should be used going forwards. This patch has been heavily rewritten from the original version by Gaurav Kashyap and Barani Muthukumaran . Signed-off-by: Eric Biggers --- Documentation/filesystems/fscrypt.rst | 154 +++++++++++++++++++++++--- fs/crypto/fscrypt_private.h | 72 ++++++++++-- fs/crypto/hkdf.c | 4 +- fs/crypto/inline_crypt.c | 77 ++++++++++++- fs/crypto/keyring.c | 119 ++++++++++++++------ fs/crypto/keysetup.c | 71 +++++++++++- fs/crypto/keysetup_v1.c | 5 +- fs/crypto/policy.c | 11 +- include/uapi/linux/fscrypt.h | 7 +- 9 files changed, 447 insertions(+), 73 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 4d5d50dca65c6..45623983578f2 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -70,7 +70,7 @@ Online attacks -------------- fscrypt (and storage encryption in general) can only provide limited -protection, if any at all, against online attacks. In detail: +protection against online attacks. In detail: Side-channel attacks ~~~~~~~~~~~~~~~~~~~~ @@ -99,16 +99,23 @@ Therefore, any encryption-specific access control checks would merely be enforced by kernel *code* and therefore would be largely redundant with the wide variety of access control mechanisms already available.) -Kernel memory compromise -~~~~~~~~~~~~~~~~~~~~~~~~ +Read-only kernel memory compromise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unless `hardware-wrapped keys`_ are used, an attacker who gains the +ability to read from arbitrary kernel memory, e.g. by mounting a +physical attack or by exploiting a kernel security vulnerability, can +compromise all fscrypt keys that are currently in-use. This also +extends to cold boot attacks; if the system is suddenly powered off, +keys the system was using may remain in memory for a short time. -An attacker who compromises the system enough to read from arbitrary -memory, e.g. by mounting a physical attack or by exploiting a kernel -security vulnerability, can compromise all encryption keys that are -currently in use. +However, if hardware-wrapped keys are used, then the fscrypt master +keys and file contents encryption keys (but not other types of fscrypt +subkeys such as filenames encryption keys) are protected from +compromises of arbitrary kernel memory. -However, fscrypt allows encryption keys to be removed from the kernel, -which may protect them from later compromise. +In addition, fscrypt allows encryption keys to be removed from the +kernel, which may protect them from later compromise. In more detail, the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl (or the FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS ioctl) can wipe a master @@ -145,6 +152,24 @@ However, these ioctls have some limitations: accelerator hardware (if used by the crypto API to implement any of the algorithms), or in other places not explicitly considered here. +Full system compromise +~~~~~~~~~~~~~~~~~~~~~~ + +An attacker who gains "root" access and/or the ability to execute +arbitrary kernel code can freely exfiltrate data that is protected by +any in-use fscrypt keys. Thus, usually fscrypt provides no meaningful +protection in this scenario. (Data that is protected by a key that is +absent throughout the entire attack remains protected, modulo the +limitations of key removal mentioned above in the case where the key +was removed prior to the attack.) + +However, if `hardware-wrapped keys`_ are used, such attackers will be +unable to exfiltrate the master keys or file contents keys in a form +that will be usable after the system is powered off. This may be +useful if the attacker is significantly time-limited and/or +bandwidth-limited, so they can only exfiltrate some data and need to +rely on a later offline attack to exfiltrate the rest of it. + Limitations of v1 policies ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -171,6 +196,11 @@ policies on all new encrypted directories. Key hierarchy ============= +Note: this section assumes the use of standard keys (i.e. "software +keys") rather than hardware-wrapped keys. The use of hardware-wrapped +keys modifies the key hierarchy slightly. For details, see the +`Hardware-wrapped keys`_ section. + Master Keys ----------- @@ -486,6 +516,8 @@ This structure must be initialized as follows: policies`_. - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32: See `IV_INO_LBLK_32 policies`_. + - FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY: This flag denotes that this + policy uses a hardware-wrapped key. See `Hardware-wrapped keys`_. v1 encryption policies only support the PAD_* and DIRECT_KEY flags. The other flags are only supported by v2 encryption policies. @@ -685,7 +717,8 @@ a pointer to struct fscrypt_add_key_arg, defined as follows:: struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; - __u32 __reserved[8]; + __u32 flags; + __u32 __reserved[7]; __u8 raw[]; }; @@ -704,7 +737,7 @@ a pointer to struct fscrypt_add_key_arg, defined as follows:: struct fscrypt_provisioning_key_payload { __u32 type; - __u32 __reserved; + __u32 flags; __u8 raw[]; }; @@ -732,6 +765,12 @@ as follows: Alternatively, if ``key_id`` is nonzero, this field must be 0, since in that case the size is implied by the specified Linux keyring key. +- ``flags`` contains optional flags from ````: + + - FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED: This denotes that the key is a + hardware-wrapped key. See `Hardware-wrapped keys`_. This flag + can't be used if FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR is used. + - ``key_id`` is 0 if the raw key is given directly in the ``raw`` field. Otherwise ``key_id`` is the ID of a Linux keyring key of type "fscrypt-provisioning" whose payload is @@ -773,6 +812,8 @@ FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors: caller does not have the CAP_SYS_ADMIN capability in the initial user namespace; or the raw key was specified by Linux key ID but the process lacks Search permission on the key. +- ``EBADMSG``: FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED was specified, but the + key isn't a valid hardware-wrapped key - ``EDQUOT``: the key quota for this user would be exceeded by adding the key - ``EINVAL``: invalid key size or key specifier type, or reserved bits @@ -784,7 +825,9 @@ FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors: - ``ENOTTY``: this type of filesystem does not implement encryption - ``EOPNOTSUPP``: the kernel was not configured with encryption support for this filesystem, or the filesystem superblock has not - had encryption enabled on it + had encryption enabled on it, or FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED was + specified but the filesystem and/or the hardware doesn't support + hardware-wrapped keys Legacy method ~~~~~~~~~~~~~ @@ -847,9 +890,8 @@ or removed by non-root users. These ioctls don't work on keys that were added via the legacy process-subscribed keyrings mechanism. -Before using these ioctls, read the `Kernel memory compromise`_ -section for a discussion of the security goals and limitations of -these ioctls. +Before using these ioctls, read the `Online attacks`_ section for a +discussion of the security goals and limitations of these ioctls. FS_IOC_REMOVE_ENCRYPTION_KEY ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1169,7 +1211,8 @@ inline encryption hardware doesn't have the needed crypto capabilities (e.g. support for the needed encryption algorithm and data unit size) and where blk-crypto-fallback is unusable. (For blk-crypto-fallback to be usable, it must be enabled in the kernel configuration with -CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y.) +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y, and the file must be +protected by a standard key rather than a hardware-wrapped key.) Currently fscrypt always uses the filesystem block size (which is usually 4096 bytes) as the data unit size. Therefore, it can only use @@ -1177,7 +1220,84 @@ inline encryption hardware that supports that data unit size. Inline encryption doesn't affect the ciphertext or other aspects of the on-disk format, so users may freely switch back and forth between -using "inlinecrypt" and not using "inlinecrypt". +using "inlinecrypt" and not using "inlinecrypt". An exception is that +files that are protected by a hardware-wrapped key can only be +encrypted/decrypted by the inline encryption hardware and therefore +can only be accessed when the "inlinecrypt" mount option is used. For +more information about hardware-wrapped keys, see below. + +Hardware-wrapped keys +--------------------- + +fscrypt supports using *hardware-wrapped keys* when the inline +encryption hardware supports it. Such keys are only present in kernel +memory in wrapped (encrypted) form; they can only be unwrapped +(decrypted) by the inline encryption hardware and are temporally bound +to the current boot. This prevents the keys from being compromised if +kernel memory is leaked. This is done without limiting the number of +keys that can be used and while still allowing the execution of +cryptographic tasks that are tied to the same key but can't use inline +encryption hardware, e.g. filenames encryption. + +Note that hardware-wrapped keys aren't specific to fscrypt per se; +they are a block layer feature (part of *blk-crypto*), which in +principle could be used by other storage encryption systems such as +dm-crypt. For more details about hardware-wrapped keys, see the block +layer documentation at :ref:`Documentation/block/inline-encryption.rst +`. Below, we just focus on the details of how +fscrypt can use hardware-wrapped keys. + +fscrypt supports hardware-wrapped keys by allowing the fscrypt master +keys to be hardware-wrapped keys as an alternative to standard keys. +To add a hardware-wrapped key with `FS_IOC_ADD_ENCRYPTION_KEY`_, +userspace must specify FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED in the +``flags`` field of struct fscrypt_add_key_arg and also in the +``flags`` field of struct fscrypt_provisioning_key_payload when +applicable. + +To specify that files will be protected by a hardware-wrapped key, +userspace must specify FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY in the +encryption policy. (Note that this flag is somewhat redundant, as the +encryption policy also contains the key identifier, and +hardware-wrapped keys and standard keys will have different key +identifiers. However, it is sometimes helpful to make it explicit +that an encryption policy is supposed to use a hardware-wrapped key.) + +Some limitations apply. First, files protected by a hardware-wrapped +key are tied to the system's inline encryption hardware. Therefore +they can only be accessed when the "inlinecrypt" mount option is used, +and they can't be included in portable filesystem images. Second, +currently the hardware-wrapped key support is only compatible with +`IV_INO_LBLK_64 policies`_ and `IV_INO_LBLK_32 policies`_, as it +assumes that there is just one file contents encryption key per +fscrypt master key rather than one per file. Future work may address +this limitation by passing per-file nonces down the storage stack to +allow the hardware to derive per-file keys. + +Implementation-wise, to encrypt/decrypt the contents of files that are +protected by a hardware-wrapped key, fscrypt uses blk-crypto, +attaching the hardware-wrapped key to the bio crypt contexts. As is +the case with standard keys, the block layer will program the key into +a keyslot when it isn't already in one. However, when programming a +hardware-wrapped key, the hardware doesn't program the given key +directly into a keyslot but rather unwraps it (using the hardware's +ephemeral wrapping key) and derives the inline encryption key from it. +The inline encryption key is the key that actually gets programmed +into a keyslot, and it is never exposed to software. + +However, fscrypt doesn't just do file contents encryption; it also +uses its master keys to derive filenames encryption keys, key +identifiers, and sometimes some more obscure types of subkeys such as +dirhash keys. So even with file contents encryption out of the +picture, fscrypt still needs a raw key to work with. To get such a +key from a hardware-wrapped key, fscrypt asks the inline encryption +hardware to derive a cryptographically isolated "software secret" from +the hardware-wrapped key. fscrypt uses this "software secret" to key +its KDF to derive all subkeys other than file contents keys. + +Note that this implies that the hardware-wrapped key feature only +protects the file contents encryption keys. It doesn't protect other +fscrypt subkeys such as filenames encryption keys. Implementation details ====================== diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 5b0a9e6478b5d..01e0a7d8177e9 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -27,6 +27,27 @@ */ #define FSCRYPT_MIN_KEY_SIZE 16 +/* Maximum size of a standard fscrypt master key */ +#define FSCRYPT_MAX_STANDARD_KEY_SIZE 64 + +/* Maximum size of a hardware-wrapped fscrypt master key */ +#define FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE + +/* + * Maximum size of an fscrypt master key across both key types. + * This should just use max(), but max() doesn't work in a struct definition. + */ +#define FSCRYPT_MAX_ANY_KEY_SIZE \ + (FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE > FSCRYPT_MAX_STANDARD_KEY_SIZE ? \ + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_STANDARD_KEY_SIZE) + +/* + * FSCRYPT_MAX_KEY_SIZE is defined in the UAPI header, but the addition of + * hardware-wrapped keys has made it misleading as it's only for standard keys. + * Don't use it in kernel code; use one of the above constants instead. + */ +#undef FSCRYPT_MAX_KEY_SIZE + #define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V2 2 @@ -319,13 +340,16 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, * outputs are unique and cryptographically isolated, i.e. knowledge of one * output doesn't reveal another. */ -#define HKDF_CONTEXT_KEY_IDENTIFIER 1 /* info= */ +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY \ + 1 /* info= */ #define HKDF_CONTEXT_PER_FILE_ENC_KEY 2 /* info=file_nonce */ #define HKDF_CONTEXT_DIRECT_KEY 3 /* info=mode_num */ #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4 /* info=mode_num||fs_uuid */ #define HKDF_CONTEXT_DIRHASH_KEY 5 /* info=file_nonce */ #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6 /* info=mode_num||fs_uuid */ #define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info= */ +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \ + 8 /* info= */ int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, const u8 *info, unsigned int infolen, @@ -345,10 +369,16 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci) int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, + unsigned int raw_key_size, + bool is_hw_wrapped, const struct fscrypt_info *ci); void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key); +int fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); + /* * Check whether the crypto transform or blk-crypto key has been allocated in * @prep_key, depending on which encryption implementation the file will use. @@ -359,7 +389,7 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, { /* * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). + * in fscrypt_prepare_inline_crypt_key() and __fscrypt_prepare_key(). * I.e., in some cases (namely, if this prep_key is a per-mode * encryption key) another task can publish blk_key or tfm concurrently, * executing a RELEASE barrier. We need to use smp_load_acquire() here @@ -385,7 +415,8 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci) static inline int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, - const u8 *raw_key, + const u8 *raw_key, unsigned int raw_key_size, + bool is_hw_wrapped, const struct fscrypt_info *ci) { WARN_ON(1); @@ -397,6 +428,15 @@ fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) { } +static inline int +fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + fscrypt_warn(NULL, "kernel doesn't support hardware-wrapped keys"); + return -EOPNOTSUPP; +} + static inline bool fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, const struct fscrypt_info *ci) @@ -413,11 +453,23 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, struct fscrypt_master_key_secret { /* - * For v2 policy keys: HKDF context keyed by this master key. - * For v1 policy keys: not set (hkdf.hmac_tfm == NULL). + * The KDF with which subkeys of this key can be derived. + * + * For v1 policy keys, this isn't applicable and won't be set. + * Otherwise, this KDF will be keyed by this master key if + * ->is_hw_wrapped=false, or by the "software secret" that hardware + * derived from this master key if ->is_hw_wrapped=true. */ struct fscrypt_hkdf hkdf; + /* + * True if this key is a hardware-wrapped key; false if this key is a + * standard key (i.e. a "software key"). For v1 policy keys this will + * always be false, as v1 policy support is a legacy feature which + * doesn't support newer functionality such as hardware-wrapped keys. + */ + bool is_hw_wrapped; + /* * Size of the raw key in bytes. This remains set even if ->raw was * zeroized due to no longer being needed. I.e. we still remember the @@ -425,8 +477,14 @@ struct fscrypt_master_key_secret { */ u32 size; - /* For v1 policy keys: the raw key. Wiped for v2 policy keys. */ - u8 raw[FSCRYPT_MAX_KEY_SIZE]; + /* + * The raw key which userspace provided, when still needed. This can be + * either a standard key or a hardware-wrapped key, as indicated by + * ->is_hw_wrapped. In the case of a standard, v2 policy key, there is + * no need to remember the raw key separately from ->hkdf so this field + * will be zeroized as soon as ->hkdf is initialized. + */ + u8 raw[FSCRYPT_MAX_ANY_KEY_SIZE]; } __randomize_layout; diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c index 7607d18b35fc0..41e7c9b05c2aa 100644 --- a/fs/crypto/hkdf.c +++ b/fs/crypto/hkdf.c @@ -4,7 +4,9 @@ * 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. + * This is used to derive keys from the fscrypt master keys (or from the + * "software secrets" which hardware derives from the fscrypt master keys, in + * the case that the fscrypt master keys are hardware-wrapped keys). * * Copyright 2019 Google LLC */ diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index ecb0cda469880..41e5cde77c197 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -68,6 +69,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) { const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; + unsigned int policy_flags = fscrypt_policy_flags(&ci->ci_policy); struct blk_crypto_config crypto_cfg; int num_devs; struct request_queue **devs; @@ -93,8 +95,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) * doesn't work with IV_INO_LBLK_32. For now, simply exclude * IV_INO_LBLK_32 with blocksize != PAGE_SIZE from inline encryption. */ - if ((fscrypt_policy_flags(&ci->ci_policy) & - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && + if ((policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && sb->s_blocksize != PAGE_SIZE) return 0; @@ -105,7 +106,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; crypto_cfg.data_unit_size = sb->s_blocksize; crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); - crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_STANDARD; + crypto_cfg.key_type = + (policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY) ? + BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_STANDARD; num_devs = fscrypt_get_num_devices(sb); devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); if (!devs) @@ -126,11 +129,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, + unsigned int raw_key_size, + bool is_hw_wrapped, const struct fscrypt_info *ci) { const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; + enum blk_crypto_key_type key_type = is_hw_wrapped ? + BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_STANDARD; int num_devs = fscrypt_get_num_devices(sb); int queue_refs = 0; struct fscrypt_blk_crypto_key *blk_key; @@ -144,8 +151,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, blk_key->num_devs = num_devs; fscrypt_get_devices(sb, num_devs, blk_key->devs); - err = blk_crypto_init_key(&blk_key->base, raw_key, ci->ci_mode->keysize, - BLK_CRYPTO_KEY_TYPE_STANDARD, crypto_mode, + err = blk_crypto_init_key(&blk_key->base, raw_key, raw_key_size, + key_type, crypto_mode, fscrypt_get_dun_bytes(ci), sb->s_blocksize); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); @@ -205,6 +212,66 @@ void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) } } +/* + * Ask the inline encryption hardware to derive the software secret from a + * hardware-wrapped key. Returns -EOPNOTSUPP if hardware-wrapped keys aren't + * supported on this filesystem or hardware. + */ +int fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + struct blk_crypto_profile *profile; + int num_devs; + int err; + + /* The filesystem must be mounted with -o inlinecrypt */ + if (!(sb->s_flags & SB_INLINECRYPT)) { + fscrypt_warn(NULL, + "%s: filesystem not mounted with inlinecrypt\n", + sb->s_id); + return -EOPNOTSUPP; + } + + /* + * Hardware-wrapped keys might be specific to a particular storage + * device, so for now we don't allow them to be used if the filesystem + * uses block devices with different crypto profiles. This way, there + * is no ambiguity about which ->derive_sw_secret method to call. + */ + profile = bdev_get_queue(sb->s_bdev)->crypto_profile; + num_devs = fscrypt_get_num_devices(sb); + if (num_devs > 1) { + struct request_queue **devs = + kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); + int i; + + if (!devs) + return -ENOMEM; + + fscrypt_get_devices(sb, num_devs, devs); + + for (i = 0; i < num_devs; i++) { + if (devs[i]->crypto_profile != profile) { + fscrypt_warn(NULL, + "%s: unsupported multi-device configuration for hardware-wrapped keys", + sb->s_id); + kfree(devs); + return -EOPNOTSUPP; + } + } + kfree(devs); + } + + err = blk_crypto_derive_sw_secret(profile, wrapped_key, + wrapped_key_size, sw_secret); + if (err == -EOPNOTSUPP) + fscrypt_warn(NULL, + "%s: block device doesn't support hardware-wrapped keys\n", + sb->s_id); + return err; +} + bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) { return inode->i_crypt_info->ci_inlinecrypt; diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 0b3ffbb4faf4a..4d6223d348d48 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -103,11 +103,11 @@ static int fscrypt_user_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { /* - * We just charge FSCRYPT_MAX_KEY_SIZE bytes to the user's key quota for - * each key, regardless of the exact key size. The amount of memory - * actually used is greater than the size of the raw key anyway. + * We just charge FSCRYPT_MAX_STANDARD_KEY_SIZE bytes to the user's key + * quota for each key, regardless of the exact key size. The amount of + * memory actually used is greater than the size of the raw key anyway. */ - return key_payload_reserve(key, FSCRYPT_MAX_KEY_SIZE); + return key_payload_reserve(key, FSCRYPT_MAX_STANDARD_KEY_SIZE); } static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) @@ -479,20 +479,45 @@ static int add_master_key(struct super_block *sb, int err; if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err = fscrypt_init_hkdf(&secret->hkdf, secret->raw, - secret->size); - if (err) - return err; + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]; + u8 *kdf_key = secret->raw; + unsigned int kdf_key_size = secret->size; + u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY; /* - * Now that the HKDF context is initialized, the raw key is no - * longer needed. + * For standard keys, the fscrypt master key is used directly as + * the fscrypt KDF key. For hardware-wrapped keys, we have to + * pass the master key to the hardware to derive the KDF key, + * which is then only used to derive non-file-contents subkeys. + */ + if (secret->is_hw_wrapped) { + err = fscrypt_derive_sw_secret(sb, secret->raw, + secret->size, sw_secret); + if (err) + return err; + kdf_key = sw_secret; + kdf_key_size = sizeof(sw_secret); + /* + * To avoid weird behavior if someone manages to + * determine sw_secret and add it as a standard key, + * ensure that hardware-wrapped keys and standard keys + * will have different key identifiers by deriving their + * key identifiers using different KDF contexts. + */ + keyid_kdf_ctx = + HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY; + } + err = fscrypt_init_hkdf(&secret->hkdf, kdf_key, kdf_key_size); + /* + * Now that the KDF context is initialized, the raw KDF key is + * no longer needed. */ - memzero_explicit(secret->raw, secret->size); + memzero_explicit(kdf_key, kdf_key_size); + if (err) + return err; /* Calculate the key identifier */ - err = fscrypt_hkdf_expand(&secret->hkdf, - HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0, + err = fscrypt_hkdf_expand(&secret->hkdf, keyid_kdf_ctx, NULL, 0, key_spec->u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE); if (err) @@ -501,19 +526,36 @@ static int add_master_key(struct super_block *sb, return do_add_master_key(sb, secret, key_spec); } +/* + * Validate the size of an fscrypt master key being added. Note that this is + * just an initial check, as we don't know which ciphers will be used yet. + * There is a stricter size check later when the key is actually used by a file. + */ +static inline bool fscrypt_valid_key_size(size_t size, u32 add_key_flags) +{ + u32 max_size = (add_key_flags & FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ? + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : + FSCRYPT_MAX_STANDARD_KEY_SIZE; + + return size >= FSCRYPT_MIN_KEY_SIZE && size <= max_size; +} + static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep) { const struct fscrypt_provisioning_key_payload *payload = prep->data; - if (prep->datalen < sizeof(*payload) + FSCRYPT_MIN_KEY_SIZE || - prep->datalen > sizeof(*payload) + FSCRYPT_MAX_KEY_SIZE) + if (prep->datalen < sizeof(*payload)) + return -EINVAL; + + if (!fscrypt_valid_key_size(prep->datalen - sizeof(*payload), + payload->flags)) return -EINVAL; if (payload->type != FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR && payload->type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) return -EINVAL; - if (payload->__reserved) + if (payload->flags & ~FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) return -EINVAL; prep->payload.data[0] = kmemdup(payload, prep->datalen, GFP_KERNEL); @@ -560,18 +602,18 @@ static struct key_type key_type_fscrypt_provisioning = { * Retrieve the raw key from the Linux keyring key specified by 'key_id', and * store it into 'secret'. * - * The key must be of type "fscrypt-provisioning" and must have the field - * fscrypt_provisioning_key_payload::type set to 'type', indicating that it's - * only usable with fscrypt with the particular KDF version identified by - * 'type'. We don't use the "logon" key type because there's no way to - * completely restrict the use of such keys; they can be used by any kernel API - * that accepts "logon" keys and doesn't require a specific service prefix. + * The key must be of type "fscrypt-provisioning" and must have the 'type' and + * 'flags' field of the payload set to the given values, indicating that the key + * is intended for use for the specified purpose. We don't use the "logon" key + * type because there's no way to completely restrict the use of such keys; they + * can be used by any kernel API that accepts "logon" keys and doesn't require a + * specific service prefix. * * The ability to specify the key via Linux keyring key is intended for cases * where userspace needs to re-add keys after the filesystem is unmounted and * re-mounted. Most users should just provide the raw key directly instead. */ -static int get_keyring_key(u32 key_id, u32 type, +static int get_keyring_key(u32 key_id, u32 type, u32 flags, struct fscrypt_master_key_secret *secret) { key_ref_t ref; @@ -588,8 +630,12 @@ static int get_keyring_key(u32 key_id, u32 type, goto bad_key; payload = key->payload.data[0]; - /* Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. */ - if (payload->type != type) + /* + * Don't allow fscrypt v1 keys to be used as v2 keys and vice versa. + * Similarly, don't allow hardware-wrapped keys to be used as + * non-hardware-wrapped keys and vice versa. + */ + if (payload->type != type || payload->flags != flags) goto bad_key; secret->size = key->datalen - sizeof(*payload); @@ -655,15 +701,24 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) return -EACCES; memset(&secret, 0, sizeof(secret)); + + if (arg.flags) { + if (arg.flags & ~FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) + return -EINVAL; + if (arg.key_spec.type != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) + return -EINVAL; + secret.is_hw_wrapped = true; + } + if (arg.key_id) { if (arg.raw_size != 0) return -EINVAL; - err = get_keyring_key(arg.key_id, arg.key_spec.type, &secret); + err = get_keyring_key(arg.key_id, arg.key_spec.type, arg.flags, + &secret); if (err) goto out_wipe_secret; } else { - if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || - arg.raw_size > FSCRYPT_MAX_KEY_SIZE) + if (!fscrypt_valid_key_size(arg.raw_size, arg.flags)) return -EINVAL; secret.size = arg.raw_size; err = -EFAULT; @@ -696,15 +751,15 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key); int fscrypt_add_test_dummy_key(struct super_block *sb, struct fscrypt_key_specifier *key_spec) { - static u8 test_key[FSCRYPT_MAX_KEY_SIZE]; + static u8 test_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; struct fscrypt_master_key_secret secret; int err; - get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE); + get_random_once(test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE); memset(&secret, 0, sizeof(secret)); - secret.size = FSCRYPT_MAX_KEY_SIZE; - memcpy(secret.raw, test_key, FSCRYPT_MAX_KEY_SIZE); + secret.size = FSCRYPT_MAX_STANDARD_KEY_SIZE; + memcpy(secret.raw, test_key, FSCRYPT_MAX_STANDARD_KEY_SIZE); err = add_master_key(sb, &secret, key_spec); wipe_master_key_secret(&secret); diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index eede186b04ce3..97ae31a054c24 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -124,15 +124,23 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, * Prepare the crypto transform object or blk-crypto key in @prep_key, given the * raw key, encryption mode (@ci->ci_mode), flag indicating which encryption * implementation (fs-layer or blk-crypto) will be used (@ci->ci_inlinecrypt), - * and IV generation method (@ci->ci_policy.flags). + * and IV generation method (@ci->ci_policy.flags). The raw key can be either a + * standard key or a hardware-wrapped key, as indicated by @is_hw_wrapped; it + * can only be a hardware-wrapped key if blk-crypto will be used. */ -int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, - const u8 *raw_key, const struct fscrypt_info *ci) +static int __fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, + const u8 *raw_key, unsigned int raw_key_size, + bool is_hw_wrapped, + const struct fscrypt_info *ci) { struct crypto_skcipher *tfm; if (fscrypt_using_inline_encryption(ci)) - return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci); + return fscrypt_prepare_inline_crypt_key(prep_key, + raw_key, raw_key_size, is_hw_wrapped, ci); + + if (WARN_ON(is_hw_wrapped || raw_key_size != ci->ci_mode->keysize)) + return -EINVAL; tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) @@ -147,6 +155,13 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, return 0; } +int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, + const u8 *raw_key, const struct fscrypt_info *ci) +{ + return __fscrypt_prepare_key(prep_key, raw_key, ci->ci_mode->keysize, + false, ci); +} + /* Destroy a crypto transform object and/or blk-crypto key. */ void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key) { @@ -171,14 +186,29 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; + u8 mode_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; + bool use_hw_wrapped_key = false; int err; if (WARN_ON(mode_num > FSCRYPT_MODE_MAX)) return -EINVAL; + if (mk->mk_secret.is_hw_wrapped && S_ISREG(inode->i_mode)) { + /* Using a hardware-wrapped key for file contents encryption */ + if (!fscrypt_using_inline_encryption(ci)) { + if (sb->s_flags & SB_INLINECRYPT) + fscrypt_warn(ci->ci_inode, + "Hardware-wrapped key required, but no suitable inline encryption hardware is available"); + else + fscrypt_warn(ci->ci_inode, + "Hardware-wrapped keys require inline encryption (-o inlinecrypt)"); + return -EINVAL; + } + use_hw_wrapped_key = true; + } + prep_key = &keys[mode_num]; if (fscrypt_is_key_prepared(prep_key, ci)) { ci->ci_enc_key = *prep_key; @@ -190,6 +220,14 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, if (fscrypt_is_key_prepared(prep_key, ci)) goto done_unlock; + if (use_hw_wrapped_key) { + err = __fscrypt_prepare_key(prep_key, mk->mk_secret.raw, + mk->mk_secret.size, true, ci); + if (err) + goto out_unlock; + goto done_unlock; + } + BUILD_BUG_ON(sizeof(mode_num) != 1); BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); BUILD_BUG_ON(sizeof(hkdf_info) != 17); @@ -312,6 +350,19 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, { int err; + if (mk->mk_secret.is_hw_wrapped && + !(ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY)) { + fscrypt_warn(ci->ci_inode, + "Key is hardware-wrapped but file isn't protected by a hardware-wrapped key"); + return -EINVAL; + } + if ((ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY) && + !mk->mk_secret.is_hw_wrapped) { + fscrypt_warn(ci->ci_inode, + "File is protected by a hardware-wrapped key, but key isn't hardware-wrapped"); + return -EINVAL; + } + if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) { /* * DIRECT_KEY: instead of deriving per-file encryption keys, the @@ -338,7 +389,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); } else { - u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; + u8 derived_key[FSCRYPT_MAX_STANDARD_KEY_SIZE]; err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_PER_FILE_ENC_KEY, @@ -474,6 +525,14 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, switch (ci->ci_policy.version) { case FSCRYPT_POLICY_V1: + if (WARN_ON(mk->mk_secret.is_hw_wrapped)) { + /* + * This should never happen, as adding a v1 policy key + * that is hardware-wrapped isn't allowed. + */ + err = -EINVAL; + goto out_release_key; + } err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw); break; case FSCRYPT_POLICY_V2: diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 2762c53504323..b2f9031de2c0e 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -118,7 +118,8 @@ find_and_lock_process_key(const char *prefix, payload = (const struct fscrypt_key *)ukp->data; if (ukp->datalen != sizeof(struct fscrypt_key) || - payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) { + payload->size < 1 || + payload->size > FSCRYPT_MAX_STANDARD_KEY_SIZE) { fscrypt_warn(NULL, "key with description '%s' has invalid payload", key->description); @@ -148,7 +149,7 @@ struct fscrypt_direct_key { const struct fscrypt_mode *dk_mode; struct fscrypt_prepared_key dk_key; u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; - u8 dk_raw[FSCRYPT_MAX_KEY_SIZE]; + u8 dk_raw[FSCRYPT_MAX_STANDARD_KEY_SIZE]; }; static void free_direct_key(struct fscrypt_direct_key *dk) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index ed3d623724cdd..9e28b6ea6bff5 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -178,7 +178,8 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK | FSCRYPT_POLICY_FLAG_DIRECT_KEY | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 | + FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY)) { fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)", policy->flags); return false; @@ -193,6 +194,14 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, return false; } + if ((policy->flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY) && + !(policy->flags & (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))) { + fscrypt_warn(inode, + "HW_WRAPPED_KEY flag can only be used with IV_INO_LBLK_64 or IV_INO_LBLK_32"); + return false; + } + if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) && !supported_direct_key_modes(inode, policy->contents_encryption_mode, policy->filenames_encryption_mode)) diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 9f4428be3e362..884c5bf526a05 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -20,6 +20,7 @@ #define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 0x10 +#define FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY 0x20 /* Encryption algorithms */ #define FSCRYPT_MODE_AES_256_XTS 1 @@ -115,7 +116,7 @@ struct fscrypt_key_specifier { */ struct fscrypt_provisioning_key_payload { __u32 type; - __u32 __reserved; + __u32 flags; __u8 raw[]; }; @@ -124,7 +125,9 @@ struct fscrypt_add_key_arg { struct fscrypt_key_specifier key_spec; __u32 raw_size; __u32 key_id; - __u32 __reserved[8]; +#define FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED 0x00000001 + __u32 flags; + __u32 __reserved[7]; __u8 raw[]; };