mbox series

[v11,0/7] Support for hardware-wrapped inline encryption keys

Message ID 20250204060041.409950-1-ebiggers@kernel.org
Headers show
Series Support for hardware-wrapped inline encryption keys | expand

Message

Eric Biggers Feb. 4, 2025, 6 a.m. UTC
This patchset is based on v6.14-rc1 and is also available at:

    git fetch https://git.kernel.org/pub/scm/fs/fscrypt/linux.git wrapped-keys-v11

This patchset adds support for hardware-wrapped inline encryption keys,
a security feature supported by some SoCs.  It adds the block and
fscrypt framework for the feature as well as support for it with UFS on
Qualcomm SoCs.

This feature is described in full detail in the included Documentation
changes.  But to summarize, hardware-wrapped keys are inline encryption
keys that are wrapped (encrypted) by a key internal to the hardware so
that they can only be unwrapped (decrypted) by the hardware.  Initially
keys are wrapped with a permanent hardware key, but during actual use
they are re-wrapped with a per-boot ephemeral key for improved security.
The hardware supports importing keys as well as generating keys itself.

This differs from the existing support for hardware-wrapped keys in the
kernel crypto API (also called "hardware-bound keys" in some places) in
the same way that the crypto API differs from blk-crypto: the crypto API
is for general crypto operations, whereas blk-crypto is for inline
storage encryption.

This feature is already being used by Android downstream for several
years
(https://source.android.com/docs/security/features/encryption/hw-wrapped-keys),
but on other platforms userspace support will be provided via fscryptctl
and tests via xfstests.  The tests have been merged into xfstests, and
they pass on the SM8650 HDK with the upstream kernel plus this patchset.

This is targeting 6.15.  As per the suggestion from Jens
(https://lore.kernel.org/linux-block/c3407d1c-6c5c-42ee-b446-ccbab1643a62@kernel.dk/),
I'd like patches 1-3 to be queued up into a branch that gets pulled into
the block tree.  I'll then take patches 4-7 through the fscrypt tree,
also for 6.15.  If I end up with too many merge conflicts by trying to
take patches 5-7 (given that this is a cross-subsystem feature), my
fallback plan will be to wait until 6.16 to land patches 5-7, when they
will finally be unblocked by the block patches having landed.

Changed in v11:
  - Rebased onto v6.14-rc1.  Dropped the patches that were upstreamed in
    6.14, and put the block patches first in the series again.
  - Significantly cleaned up the patch "soc: qcom: ice: add HWKM support
    to the ICE driver".  Some of the notable changes were dropping the
    unnecessary support for HWKM v1, and replacing qcom_ice_using_hwkm()
    with qcom_ice_get_supported_key_type().
  - Consistently used and documented the EBADMSG error code for invalid
    hardware-wrapped keys.
  - Other minor cleanups.

Changed in v10:
  - Fixed bugs in qcom_scm_derive_sw_secret() and cqhci_crypto_init().
  - Added "ufs: qcom: fix crypto key eviction" and
    "mmc: sdhci-msm: fix crypto key eviction".
  - Split removing ufs_hba_variant_ops::program_key into its own patch.
  - Minor cleanups.
  - Added Tested-by.

Changed in v9 (relative to v7 patchset from Bartosz Golaszewski):
  - ufs-qcom and sdhci-msm now just initialize the blk_crypto_profile
    themselves, like what ufs-exynos was doing.  This avoids needing to
    add all the host-specific hooks for wrapped key support to the MMC
    and UFS core drivers.
  - When passing the blk_crypto_key further down the stack, it now
    replaces parameters like the algorithm ID, to avoid creating two
    sources of truth.
  - The module parameter qcom_ice.use_wrapped_keys should work correctly now.
  - The fscrypt support no longer uses a policy flag to indicate when a
    file is protected by a HW-wrapped key, since it was already implied
    by the file's key identifier being that of a HW-wrapped key.
    Originally there was an issue where raw and HW-wrapped keys could
    share key identifiers, but I had fixed that earlier by introducing a
    new HKDF context byte.
  - The term "standard keys" is no longer used.  Now "raw keys" is
    consistently used instead.  I've found that people find the term
    "raw keys" to be more intuitive.  Also HW-wrapped keys could in
    principle be standardized.
  - I've reordered the patchset to place preparatory patches that don't
    depend on the actual HW-wrapped key support first.

For older changelogs, see
https://lore.kernel.org/r/20241202-wrapped-keys-v7-0-67c3ca3f3282@linaro.org and
https://lore.kernel.org/r/20231104211259.17448-1-ebiggers@kernel.org

Eric Biggers (6):
  blk-crypto: add basic hardware-wrapped key support
  blk-crypto: show supported key types in sysfs
  blk-crypto: add ioctls to create and prepare hardware-wrapped keys
  fscrypt: add support for hardware-wrapped keys
  soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key
  ufs: qcom: add support for wrapped keys

Gaurav Kashyap (1):
  soc: qcom: ice: add HWKM support to the ICE driver

 Documentation/ABI/stable/sysfs-block          |  20 +
 Documentation/block/inline-encryption.rst     | 255 ++++++++++++-
 Documentation/filesystems/fscrypt.rst         | 200 ++++++++--
 .../userspace-api/ioctl/ioctl-number.rst      |   2 +
 block/blk-crypto-fallback.c                   |   7 +-
 block/blk-crypto-internal.h                   |  10 +
 block/blk-crypto-profile.c                    | 101 +++++
 block/blk-crypto-sysfs.c                      |  35 ++
 block/blk-crypto.c                            | 204 +++++++++-
 block/ioctl.c                                 |   5 +
 drivers/md/dm-table.c                         |   1 +
 drivers/mmc/host/cqhci-crypto.c               |   8 +-
 drivers/mmc/host/sdhci-msm.c                  |  17 +-
 drivers/soc/qcom/ice.c                        | 349 ++++++++++++++++--
 drivers/ufs/core/ufshcd-crypto.c              |   7 +-
 drivers/ufs/host/ufs-exynos.c                 |   3 +-
 drivers/ufs/host/ufs-qcom.c                   |  56 ++-
 fs/crypto/fscrypt_private.h                   |  75 +++-
 fs/crypto/hkdf.c                              |   4 +-
 fs/crypto/inline_crypt.c                      |  42 ++-
 fs/crypto/keyring.c                           | 157 ++++++--
 fs/crypto/keysetup.c                          |  63 +++-
 fs/crypto/keysetup_v1.c                       |   4 +-
 include/linux/blk-crypto-profile.h            |  73 ++++
 include/linux/blk-crypto.h                    |  73 +++-
 include/soc/qcom/ice.h                        |  34 +-
 include/uapi/linux/blk-crypto.h               |  44 +++
 include/uapi/linux/fs.h                       |   6 +-
 include/uapi/linux/fscrypt.h                  |   7 +-
 29 files changed, 1654 insertions(+), 208 deletions(-)
 create mode 100644 include/uapi/linux/blk-crypto.h


base-commit: 2014c95afecee3e76ca4a56956a936e23283f05b

Comments

Manivannan Sadhasivam Feb. 9, 2025, 3:50 p.m. UTC | #1
On Mon, Feb 03, 2025 at 10:00:39PM -0800, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> qcom_ice_program_key() currently accepts the key as an array of bytes,
> algorithm ID, key size enum, and data unit size.  However both callers
> have a struct blk_crypto_key which contains all that information.  Thus
> they both have similar code that converts the blk_crypto_key into the
> form that qcom_ice_program_key() wants.  Once wrapped key support is
> added, the key type would need to be added to the arguments too.
> 
> Therefore, this patch changes qcom_ice_program_key() to take in all this
> information as a struct blk_crypto_key directly.  The calling code is
> updated accordingly.  This ends up being much simpler, and it makes the
> key type be passed down automatically once wrapped key support is added.
> 
> Based on a patch by Gaurav Kashyap <quic_gaurkash@quicinc.com> that
> replaced the byte array argument only.  This patch makes the
> blk_crypto_key replace other arguments like the algorithm ID too,
> ensuring that there remains only one source of truth.
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>

For ufs-qcom:

Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>

- Mani

> ---
>  drivers/mmc/host/sdhci-msm.c | 11 +----------
>  drivers/soc/qcom/ice.c       | 23 ++++++++++++-----------
>  drivers/ufs/host/ufs-qcom.c  | 11 +----------
>  include/soc/qcom/ice.h       | 22 +++-------------------
>  4 files changed, 17 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
> index 3c383bce4928f..2c926f566d053 100644
> --- a/drivers/mmc/host/sdhci-msm.c
> +++ b/drivers/mmc/host/sdhci-msm.c
> @@ -1960,20 +1960,11 @@ static int sdhci_msm_ice_keyslot_program(struct blk_crypto_profile *profile,
>  					 unsigned int slot)
>  {
>  	struct sdhci_msm_host *msm_host =
>  		sdhci_msm_host_from_crypto_profile(profile);
>  
> -	/* Only AES-256-XTS has been tested so far. */
> -	if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS)
> -		return -EOPNOTSUPP;
> -
> -	return qcom_ice_program_key(msm_host->ice,
> -				    QCOM_ICE_CRYPTO_ALG_AES_XTS,
> -				    QCOM_ICE_CRYPTO_KEY_SIZE_256,
> -				    key->bytes,
> -				    key->crypto_cfg.data_unit_size / 512,
> -				    slot);
> +	return qcom_ice_program_key(msm_host->ice, slot, key);
>  }
>  
>  static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,
>  				       const struct blk_crypto_key *key,
>  				       unsigned int slot)
> diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
> index 393d2d1d275f1..78780fd508f0b 100644
> --- a/drivers/soc/qcom/ice.c
> +++ b/drivers/soc/qcom/ice.c
> @@ -159,41 +159,42 @@ int qcom_ice_suspend(struct qcom_ice *ice)
>  
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(qcom_ice_suspend);
>  
> -int qcom_ice_program_key(struct qcom_ice *ice,
> -			 u8 algorithm_id, u8 key_size,
> -			 const u8 crypto_key[], u8 data_unit_size,
> -			 int slot)
> +int qcom_ice_program_key(struct qcom_ice *ice, unsigned int slot,
> +			 const struct blk_crypto_key *blk_key)
>  {
>  	struct device *dev = ice->dev;
>  	union {
>  		u8 bytes[AES_256_XTS_KEY_SIZE];
>  		u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
>  	} key;
>  	int i;
>  	int err;
>  
>  	/* Only AES-256-XTS has been tested so far. */
> -	if (algorithm_id != QCOM_ICE_CRYPTO_ALG_AES_XTS ||
> -	    key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256) {
> -		dev_err_ratelimited(dev,
> -				    "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
> -				    algorithm_id, key_size);
> +	if (blk_key->crypto_cfg.crypto_mode !=
> +	    BLK_ENCRYPTION_MODE_AES_256_XTS) {
> +		dev_err_ratelimited(dev, "Unsupported crypto mode: %d\n",
> +				    blk_key->crypto_cfg.crypto_mode);
>  		return -EINVAL;
>  	}
>  
> -	memcpy(key.bytes, crypto_key, AES_256_XTS_KEY_SIZE);
> +	if (blk_key->size != AES_256_XTS_KEY_SIZE) {
> +		dev_err_ratelimited(dev, "Incorrect key size\n");
> +		return -EINVAL;
> +	}
> +	memcpy(key.bytes, blk_key->bytes, AES_256_XTS_KEY_SIZE);
>  
>  	/* The SCM call requires that the key words are encoded in big endian */
>  	for (i = 0; i < ARRAY_SIZE(key.words); i++)
>  		__cpu_to_be32s(&key.words[i]);
>  
>  	err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
>  				   QCOM_SCM_ICE_CIPHER_AES_256_XTS,
> -				   data_unit_size);
> +				   blk_key->crypto_cfg.data_unit_size / 512);
>  
>  	memzero_explicit(&key, sizeof(key));
>  
>  	return err;
>  }
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index c3f0aa81ff983..9330022e98eec 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -193,21 +193,12 @@ static int ufs_qcom_ice_keyslot_program(struct blk_crypto_profile *profile,
>  {
>  	struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
>  	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
>  	int err;
>  
> -	/* Only AES-256-XTS has been tested so far. */
> -	if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS)
> -		return -EOPNOTSUPP;
> -
>  	ufshcd_hold(hba);
> -	err = qcom_ice_program_key(host->ice,
> -				   QCOM_ICE_CRYPTO_ALG_AES_XTS,
> -				   QCOM_ICE_CRYPTO_KEY_SIZE_256,
> -				   key->bytes,
> -				   key->crypto_cfg.data_unit_size / 512,
> -				   slot);
> +	err = qcom_ice_program_key(host->ice, slot, key);
>  	ufshcd_release(hba);
>  	return err;
>  }
>  
>  static int ufs_qcom_ice_keyslot_evict(struct blk_crypto_profile *profile,
> diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h
> index 5870a94599a25..4cecc7f088b4b 100644
> --- a/include/soc/qcom/ice.h
> +++ b/include/soc/qcom/ice.h
> @@ -4,34 +4,18 @@
>   */
>  
>  #ifndef __QCOM_ICE_H__
>  #define __QCOM_ICE_H__
>  
> +#include <linux/blk-crypto.h>
>  #include <linux/types.h>
>  
>  struct qcom_ice;
>  
> -enum qcom_ice_crypto_key_size {
> -	QCOM_ICE_CRYPTO_KEY_SIZE_INVALID	= 0x0,
> -	QCOM_ICE_CRYPTO_KEY_SIZE_128		= 0x1,
> -	QCOM_ICE_CRYPTO_KEY_SIZE_192		= 0x2,
> -	QCOM_ICE_CRYPTO_KEY_SIZE_256		= 0x3,
> -	QCOM_ICE_CRYPTO_KEY_SIZE_512		= 0x4,
> -};
> -
> -enum qcom_ice_crypto_alg {
> -	QCOM_ICE_CRYPTO_ALG_AES_XTS		= 0x0,
> -	QCOM_ICE_CRYPTO_ALG_BITLOCKER_AES_CBC	= 0x1,
> -	QCOM_ICE_CRYPTO_ALG_AES_ECB		= 0x2,
> -	QCOM_ICE_CRYPTO_ALG_ESSIV_AES_CBC	= 0x3,
> -};
> -
>  int qcom_ice_enable(struct qcom_ice *ice);
>  int qcom_ice_resume(struct qcom_ice *ice);
>  int qcom_ice_suspend(struct qcom_ice *ice);
> -int qcom_ice_program_key(struct qcom_ice *ice,
> -			 u8 algorithm_id, u8 key_size,
> -			 const u8 crypto_key[], u8 data_unit_size,
> -			 int slot);
> +int qcom_ice_program_key(struct qcom_ice *ice, unsigned int slot,
> +			 const struct blk_crypto_key *blk_key);
>  int qcom_ice_evict_key(struct qcom_ice *ice, int slot);
>  struct qcom_ice *of_qcom_ice_get(struct device *dev);
>  #endif /* __QCOM_ICE_H__ */
> -- 
> 2.48.1
>
Manivannan Sadhasivam Feb. 10, 2025, 4:44 a.m. UTC | #2
On Mon, Feb 03, 2025 at 10:00:41PM -0800, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> Wire up the wrapped key support for ufs-qcom by implementing the needed
> methods in struct blk_crypto_ll_ops and setting the appropriate flag in
> blk_crypto_profile::key_types_supported.
> 
> For more information about this feature and how to use it, refer to
> the sections about hardware-wrapped keys in
> Documentation/block/inline-encryption.rst and
> Documentation/filesystems/fscrypt.rst.
> 
> Based on patches by Gaurav Kashyap <quic_gaurkash@quicinc.com>.
> Reworked to use the custom crypto profile support.
> 

Instead of mentioning the contribution in description, you should use relevant
tags IMO.

> Signed-off-by: Eric Biggers <ebiggers@google.com>

Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>

- Mani

> ---
>  drivers/ufs/host/ufs-qcom.c | 51 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 45 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index f34527fb02fb2..dc3eb6f29f5b2 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -132,15 +132,10 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
>  	}
>  
>  	if (IS_ERR_OR_NULL(ice))
>  		return PTR_ERR_OR_ZERO(ice);
>  
> -	if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) {
> -		dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n");
> -		return 0;
> -	}
> -
>  	host->ice = ice;
>  
>  	/* Initialize the blk_crypto_profile */
>  
>  	caps.reg_val = cpu_to_le32(ufshcd_readl(hba, REG_UFS_CCAP));
> @@ -150,11 +145,11 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host)
>  	if (err)
>  		return err;
>  
>  	profile->ll_ops = ufs_qcom_crypto_ops;
>  	profile->max_dun_bytes_supported = 8;
> -	profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
> +	profile->key_types_supported = qcom_ice_get_supported_key_type(ice);
>  	profile->dev = dev;
>  
>  	/*
>  	 * Currently this driver only supports AES-256-XTS.  All known versions
>  	 * of ICE support it, but to be safe make sure it is really declared in
> @@ -218,13 +213,57 @@ static int ufs_qcom_ice_keyslot_evict(struct blk_crypto_profile *profile,
>  	err = qcom_ice_evict_key(host->ice, slot);
>  	ufshcd_release(hba);
>  	return err;
>  }
>  
> +static int ufs_qcom_ice_derive_sw_secret(struct blk_crypto_profile *profile,
> +					 const u8 *eph_key, size_t eph_key_size,
> +					 u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
> +{
> +	struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +
> +	return qcom_ice_derive_sw_secret(host->ice, eph_key, eph_key_size,
> +					 sw_secret);
> +}
> +
> +static int ufs_qcom_ice_import_key(struct blk_crypto_profile *profile,
> +				   const u8 *raw_key, size_t raw_key_size,
> +				   u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
> +{
> +	struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +
> +	return qcom_ice_import_key(host->ice, raw_key, raw_key_size, lt_key);
> +}
> +
> +static int ufs_qcom_ice_generate_key(struct blk_crypto_profile *profile,
> +				     u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
> +{
> +	struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +
> +	return qcom_ice_generate_key(host->ice, lt_key);
> +}
> +
> +static int ufs_qcom_ice_prepare_key(struct blk_crypto_profile *profile,
> +				    const u8 *lt_key, size_t lt_key_size,
> +				    u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
> +{
> +	struct ufs_hba *hba = ufs_hba_from_crypto_profile(profile);
> +	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +
> +	return qcom_ice_prepare_key(host->ice, lt_key, lt_key_size, eph_key);
> +}
> +
>  static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {
>  	.keyslot_program	= ufs_qcom_ice_keyslot_program,
>  	.keyslot_evict		= ufs_qcom_ice_keyslot_evict,
> +	.derive_sw_secret	= ufs_qcom_ice_derive_sw_secret,
> +	.import_key		= ufs_qcom_ice_import_key,
> +	.generate_key		= ufs_qcom_ice_generate_key,
> +	.prepare_key		= ufs_qcom_ice_prepare_key,
>  };
>  
>  #else
>  
>  static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host)
> -- 
> 2.48.1
>
Eric Biggers Feb. 10, 2025, 4:25 p.m. UTC | #3
Hi Jens,

On Mon, Feb 03, 2025 at 10:00:34PM -0800, Eric Biggers wrote:
> This is targeting 6.15.  As per the suggestion from Jens
> (https://lore.kernel.org/linux-block/c3407d1c-6c5c-42ee-b446-ccbab1643a62@kernel.dk/),
> I'd like patches 1-3 to be queued up into a branch that gets pulled into
> the block tree.  I'll then take patches 4-7 through the fscrypt tree,
> also for 6.15.  If I end up with too many merge conflicts by trying to
> take patches 5-7 (given that this is a cross-subsystem feature), my
> fallback plan will be to wait until 6.16 to land patches 5-7, when they
> will finally be unblocked by the block patches having landed.

Let me know what you think about this plan.

- Eric