* [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys
@ 2025-02-10 20:23 Eric Biggers
2025-02-10 20:23 ` [PATCH v12 1/4] soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key Eric Biggers
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Eric Biggers @ 2025-02-10 20:23 UTC (permalink / raw)
To: linux-fscrypt, linux-scsi
Cc: linux-block, linux-mmc, linux-arm-msm, linux-kernel,
Bartosz Golaszewski, Gaurav Kashyap, Bjorn Andersson,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam
This patchset is based on linux-block/for-next and is also available at:
git fetch https://git.kernel.org/pub/scm/fs/fscrypt/linux.git wrapped-keys-v12
Now that the block layer support for hardware-wrapped inline encryption
keys has been applied for 6.15
(https://lore.kernel.org/r/173920649542.40307.8847368467858129326.b4-ty@kernel.dk),
this series refreshes the remaining patches. They add the support for
hardware-wrapped inline encryption keys to the Qualcomm ICE and UFS
drivers and to fscrypt. All tested on SM8650 with xfstests.
TBD whether these will land in 6.15 too, or wait until 6.16 when the
block patches that patches 2-4 depend on will have landed.
Changed in v12:
- Rebased onto linux-block/for-next
- Fixed endianness error in drivers/soc/qcom/ice.c
- Added Acked-bys
- Updated the fscrypt patch to go back to having just
FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED (as in v8) instead of
FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED_V0 and
FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED_V1. Upon further discussion it
seemed the partial V0 compatibility was not going to be as helpful
as I had hoped, so instead we'll just have the single new version
that does things properly. Note, I've updated my wip-wrapped-keys
branch of fscryptctl accordingly.
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 (3):
soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key
ufs: qcom: add support for wrapped keys
fscrypt: add support for hardware-wrapped keys
Gaurav Kashyap (1):
soc: qcom: ice: add HWKM support to the ICE driver
Documentation/filesystems/fscrypt.rst | 187 +++++++++++---
drivers/mmc/host/sdhci-msm.c | 16 +-
drivers/soc/qcom/ice.c | 350 ++++++++++++++++++++++++--
drivers/ufs/host/ufs-qcom.c | 57 ++++-
fs/crypto/fscrypt_private.h | 75 +++++-
fs/crypto/hkdf.c | 4 +-
fs/crypto/inline_crypt.c | 44 +++-
fs/crypto/keyring.c | 138 +++++++---
fs/crypto/keysetup.c | 63 ++++-
fs/crypto/keysetup_v1.c | 4 +-
include/soc/qcom/ice.h | 34 ++-
include/uapi/linux/fscrypt.h | 6 +-
12 files changed, 809 insertions(+), 169 deletions(-)
base-commit: 352245090aa60dbaa11b4f7da18f31caf42aeb82
--
2.48.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v12 1/4] soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
@ 2025-02-10 20:23 ` Eric Biggers
2025-02-10 20:23 ` [PATCH v12 2/4] soc: qcom: ice: add HWKM support to the ICE driver Eric Biggers
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Eric Biggers @ 2025-02-10 20:23 UTC (permalink / raw)
To: linux-fscrypt, linux-scsi
Cc: linux-block, linux-mmc, linux-arm-msm, linux-kernel,
Bartosz Golaszewski, Gaurav Kashyap, Bjorn Andersson,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam, Bartosz Golaszewski
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.
Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Tested-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> # sm8650
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
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 3c383bce4928..2c926f566d05 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 393d2d1d275f..78780fd508f0 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 c3f0aa81ff98..9330022e98ee 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 5870a94599a2..4cecc7f088b4 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
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v12 2/4] soc: qcom: ice: add HWKM support to the ICE driver
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
2025-02-10 20:23 ` [PATCH v12 1/4] soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key Eric Biggers
@ 2025-02-10 20:23 ` Eric Biggers
2025-02-10 20:23 ` [PATCH v12 3/4] ufs: qcom: add support for wrapped keys Eric Biggers
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Eric Biggers @ 2025-02-10 20:23 UTC (permalink / raw)
To: linux-fscrypt, linux-scsi
Cc: linux-block, linux-mmc, linux-arm-msm, linux-kernel,
Bartosz Golaszewski, Gaurav Kashyap, Bjorn Andersson,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam, Bartosz Golaszewski
From: Gaurav Kashyap <quic_gaurkash@quicinc.com>
Qualcomm's Inline Crypto Engine (ICE) version 3.2 and later includes a
key management hardware block called the Hardware Key Manager (HWKM).
Add support for HWKM to the ICE driver. HWKM provides hardware-wrapped
key support where the ICE (storage) keys are not exposed to software and
instead are protected in hardware. Later patches will wire up this
feature to ufs-qcom and sdhci-msm using the support added in this patch.
HWKM and legacy mode are currently mutually exclusive. The selection of
which mode to use has to be made before the storage driver(s) registers
any inline encryption capable disk(s) with the block layer (i.e.,
generally at boot time) so that the appropriate crypto capabilities can
be advertised to upper layers. Therefore, make the ICE driver select
HWKM mode when the all of the following are true:
- The new module parameter qcom_ice.use_wrapped_keys=1 is specified.
- HWKM is present and is at least v2, i.e. ICE is v3.2.1 or later.
- The SCM calls needed to fully use HWKM are supported by TrustZone.
Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
[EB: merged related patches; fixed the module parameter to work
correctly; dropped unnecessary support for HWKM v1; fixed error
handling; improved log messages, comments, and commit message;
fixed naming; merged enable and init functions; and other cleanups]
Co-developed-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
drivers/mmc/host/sdhci-msm.c | 5 +
drivers/soc/qcom/ice.c | 327 +++++++++++++++++++++++++++++++++--
drivers/ufs/host/ufs-qcom.c | 5 +
include/soc/qcom/ice.h | 12 ++
4 files changed, 339 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 2c926f566d05..2772e34490cc 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1880,10 +1880,15 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_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;
+ }
+
msm_host->ice = ice;
/* Initialize the blk_crypto_profile */
caps.reg_val = cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index 78780fd508f0..e4ae8e09092b 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -18,36 +18,86 @@
#include <linux/firmware/qcom/qcom_scm.h>
#include <soc/qcom/ice.h>
-#define AES_256_XTS_KEY_SIZE 64
+#define AES_256_XTS_KEY_SIZE 64 /* for raw keys only */
+#define QCOM_ICE_HWKM_WRAPPED_KEY_SIZE 100 /* assuming HWKM v2 */
/* QCOM ICE registers */
+
+#define QCOM_ICE_REG_CONTROL 0x0000
+#define QCOM_ICE_LEGACY_MODE_ENABLED BIT(0)
+
#define QCOM_ICE_REG_VERSION 0x0008
+
#define QCOM_ICE_REG_FUSE_SETTING 0x0010
+#define QCOM_ICE_FUSE_SETTING_MASK BIT(0)
+#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK BIT(1)
+#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK BIT(2)
+
#define QCOM_ICE_REG_BIST_STATUS 0x0070
+#define QCOM_ICE_BIST_STATUS_MASK GENMASK(31, 28)
+
#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
-/* BIST ("built-in self-test") status flags */
-#define QCOM_ICE_BIST_STATUS_MASK GENMASK(31, 28)
+#define QCOM_ICE_REG_CRYPTOCFG_BASE 0x4040
+#define QCOM_ICE_REG_CRYPTOCFG_SIZE 0x80
+#define QCOM_ICE_REG_CRYPTOCFG(slot) (QCOM_ICE_REG_CRYPTOCFG_BASE + \
+ QCOM_ICE_REG_CRYPTOCFG_SIZE * (slot))
+union crypto_cfg {
+ __le32 regval;
+ struct {
+ u8 dusize;
+ u8 capidx;
+ u8 reserved;
+#define QCOM_ICE_HWKM_CFG_ENABLE_VAL BIT(7)
+ u8 cfge;
+ };
+};
+
+/* QCOM ICE HWKM (Hardware Key Manager) registers */
+
+#define HWKM_OFFSET 0x8000
+
+#define QCOM_ICE_REG_HWKM_TZ_KM_CTL (HWKM_OFFSET + 0x1000)
+#define QCOM_ICE_HWKM_DISABLE_CRC_CHECKS_VAL (BIT(1) | BIT(2))
-#define QCOM_ICE_FUSE_SETTING_MASK 0x1
-#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
-#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
+#define QCOM_ICE_REG_HWKM_TZ_KM_STATUS (HWKM_OFFSET + 0x1004)
+#define QCOM_ICE_HWKM_KT_CLEAR_DONE BIT(0)
+#define QCOM_ICE_HWKM_BOOT_CMD_LIST0_DONE BIT(1)
+#define QCOM_ICE_HWKM_BOOT_CMD_LIST1_DONE BIT(2)
+#define QCOM_ICE_HWKM_CRYPTO_BIST_DONE_V2 BIT(7)
+#define QCOM_ICE_HWKM_BIST_DONE_V2 BIT(9)
+
+#define QCOM_ICE_REG_HWKM_BANK0_BANKN_IRQ_STATUS (HWKM_OFFSET + 0x2008)
+#define QCOM_ICE_HWKM_RSP_FIFO_CLEAR_VAL BIT(3)
+
+#define QCOM_ICE_REG_HWKM_BANK0_BBAC_0 (HWKM_OFFSET + 0x5000)
+#define QCOM_ICE_REG_HWKM_BANK0_BBAC_1 (HWKM_OFFSET + 0x5004)
+#define QCOM_ICE_REG_HWKM_BANK0_BBAC_2 (HWKM_OFFSET + 0x5008)
+#define QCOM_ICE_REG_HWKM_BANK0_BBAC_3 (HWKM_OFFSET + 0x500C)
+#define QCOM_ICE_REG_HWKM_BANK0_BBAC_4 (HWKM_OFFSET + 0x5010)
#define qcom_ice_writel(engine, val, reg) \
writel((val), (engine)->base + (reg))
#define qcom_ice_readl(engine, reg) \
readl((engine)->base + (reg))
+static bool qcom_ice_use_wrapped_keys;
+module_param_named(use_wrapped_keys, qcom_ice_use_wrapped_keys, bool, 0660);
+MODULE_PARM_DESC(use_wrapped_keys,
+ "Support wrapped keys instead of raw keys, if available on the platform");
+
struct qcom_ice {
struct device *dev;
void __iomem *base;
struct clk *core_clk;
+ bool use_hwkm;
+ bool hwkm_init_complete;
};
static bool qcom_ice_check_supported(struct qcom_ice *ice)
{
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
@@ -73,10 +123,39 @@ static bool qcom_ice_check_supported(struct qcom_ice *ice)
QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
return false;
}
+ /*
+ * Check for HWKM support and decide whether to use it or not. ICE
+ * v3.2.1 and later have HWKM v2. ICE v3.2.0 has HWKM v1. Earlier ICE
+ * versions don't have HWKM at all. However, for HWKM to be fully
+ * usable by Linux, the TrustZone software also needs to support certain
+ * SCM calls including the ones to generate and prepare keys. That
+ * effectively makes the earliest supported SoC be SM8650, which has
+ * HWKM v2. Therefore, this driver doesn't include support for HWKM v1,
+ * and it checks for the SCM call support before it decides to use HWKM.
+ *
+ * Also, since HWKM and legacy mode are mutually exclusive, and
+ * ICE-capable storage driver(s) need to know early on whether to
+ * advertise support for raw keys or wrapped keys, HWKM cannot be used
+ * unconditionally. A module parameter is used to opt into using it.
+ */
+ if ((major >= 4 ||
+ (major == 3 && (minor >= 3 || (minor == 2 && step >= 1)))) &&
+ qcom_scm_has_wrapped_key_support()) {
+ if (qcom_ice_use_wrapped_keys) {
+ dev_info(dev, "Using HWKM. Supporting wrapped keys only.\n");
+ ice->use_hwkm = true;
+ } else {
+ dev_info(dev, "Not using HWKM. Supporting raw keys only.\n");
+ }
+ } else if (qcom_ice_use_wrapped_keys) {
+ dev_warn(dev, "A supported HWKM is not present. Ignoring qcom_ice.use_wrapped_keys=1.\n");
+ } else {
+ dev_info(dev, "A supported HWKM is not present. Supporting raw keys only.\n");
+ }
return true;
}
static void qcom_ice_low_power_mode_enable(struct qcom_ice *ice)
{
@@ -120,21 +199,75 @@ static int qcom_ice_wait_bist_status(struct qcom_ice *ice)
int err;
err = readl_poll_timeout(ice->base + QCOM_ICE_REG_BIST_STATUS,
regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
50, 5000);
- if (err)
+ if (err) {
dev_err(ice->dev, "Timed out waiting for ICE self-test to complete\n");
+ return err;
+ }
- return err;
+ if (ice->use_hwkm &&
+ qcom_ice_readl(ice, QCOM_ICE_REG_HWKM_TZ_KM_STATUS) !=
+ (QCOM_ICE_HWKM_KT_CLEAR_DONE |
+ QCOM_ICE_HWKM_BOOT_CMD_LIST0_DONE |
+ QCOM_ICE_HWKM_BOOT_CMD_LIST1_DONE |
+ QCOM_ICE_HWKM_CRYPTO_BIST_DONE_V2 |
+ QCOM_ICE_HWKM_BIST_DONE_V2)) {
+ dev_err(ice->dev, "HWKM self-test error!\n");
+ /*
+ * Too late to revoke use_hwkm here, as it was already
+ * propagated up the stack into the crypto capabilities.
+ */
+ }
+ return 0;
+}
+
+static void qcom_ice_hwkm_init(struct qcom_ice *ice)
+{
+ u32 regval;
+
+ if (!ice->use_hwkm)
+ return;
+
+ BUILD_BUG_ON(QCOM_ICE_HWKM_WRAPPED_KEY_SIZE >
+ BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE);
+ /*
+ * When ICE is in HWKM mode, it only supports wrapped keys.
+ * When ICE is in legacy mode, it only supports raw keys.
+ *
+ * Put ICE in HWKM mode. ICE defaults to legacy mode.
+ */
+ regval = qcom_ice_readl(ice, QCOM_ICE_REG_CONTROL);
+ regval &= ~QCOM_ICE_LEGACY_MODE_ENABLED;
+ qcom_ice_writel(ice, regval, QCOM_ICE_REG_CONTROL);
+
+ /* Disable CRC checks. This HWKM feature is not used. */
+ qcom_ice_writel(ice, QCOM_ICE_HWKM_DISABLE_CRC_CHECKS_VAL,
+ QCOM_ICE_REG_HWKM_TZ_KM_CTL);
+
+ /*
+ * Allow the HWKM slave to read and write the keyslots in the ICE HWKM
+ * slave. Without this, TrustZone cannot program keys into ICE.
+ */
+ qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_0);
+ qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_1);
+ qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_2);
+ qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_3);
+ qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_4);
+
+ /* Clear the HWKM response FIFO. */
+ qcom_ice_writel(ice, QCOM_ICE_HWKM_RSP_FIFO_CLEAR_VAL,
+ QCOM_ICE_REG_HWKM_BANK0_BANKN_IRQ_STATUS);
+ ice->hwkm_init_complete = true;
}
int qcom_ice_enable(struct qcom_ice *ice)
{
qcom_ice_low_power_mode_enable(ice);
qcom_ice_optimization_enable(ice);
-
+ qcom_ice_hwkm_init(ice);
return qcom_ice_wait_bist_status(ice);
}
EXPORT_SYMBOL_GPL(qcom_ice_enable);
int qcom_ice_resume(struct qcom_ice *ice)
@@ -146,23 +279,68 @@ int qcom_ice_resume(struct qcom_ice *ice)
if (err) {
dev_err(dev, "failed to enable core clock (%d)\n",
err);
return err;
}
-
+ qcom_ice_hwkm_init(ice);
return qcom_ice_wait_bist_status(ice);
}
EXPORT_SYMBOL_GPL(qcom_ice_resume);
int qcom_ice_suspend(struct qcom_ice *ice)
{
clk_disable_unprepare(ice->core_clk);
+ ice->hwkm_init_complete = false;
return 0;
}
EXPORT_SYMBOL_GPL(qcom_ice_suspend);
+static unsigned int translate_hwkm_slot(struct qcom_ice *ice, unsigned int slot)
+{
+ return slot * 2;
+}
+
+static int qcom_ice_program_wrapped_key(struct qcom_ice *ice, unsigned int slot,
+ const struct blk_crypto_key *bkey)
+{
+ struct device *dev = ice->dev;
+ union crypto_cfg cfg = {
+ .dusize = bkey->crypto_cfg.data_unit_size / 512,
+ .capidx = QCOM_SCM_ICE_CIPHER_AES_256_XTS,
+ .cfge = QCOM_ICE_HWKM_CFG_ENABLE_VAL,
+ };
+ int err;
+
+ if (!ice->use_hwkm) {
+ dev_err_ratelimited(dev, "Got wrapped key when not using HWKM\n");
+ return -EINVAL;
+ }
+ if (!ice->hwkm_init_complete) {
+ dev_err_ratelimited(dev, "HWKM not yet initialized\n");
+ return -EINVAL;
+ }
+
+ /* Clear CFGE before programming the key. */
+ qcom_ice_writel(ice, 0x0, QCOM_ICE_REG_CRYPTOCFG(slot));
+
+ /* Call into TrustZone to program the wrapped key using HWKM. */
+ err = qcom_scm_ice_set_key(translate_hwkm_slot(ice, slot), bkey->bytes,
+ bkey->size, cfg.capidx, cfg.dusize);
+ if (err) {
+ dev_err_ratelimited(dev,
+ "qcom_scm_ice_set_key failed; err=%d, slot=%u\n",
+ err, slot);
+ return err;
+ }
+
+ /* Set CFGE after programming the key. */
+ qcom_ice_writel(ice, le32_to_cpu(cfg.regval),
+ QCOM_ICE_REG_CRYPTOCFG(slot));
+ return 0;
+}
+
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 {
@@ -178,10 +356,18 @@ int qcom_ice_program_key(struct qcom_ice *ice, unsigned int slot,
dev_err_ratelimited(dev, "Unsupported crypto mode: %d\n",
blk_key->crypto_cfg.crypto_mode);
return -EINVAL;
}
+ if (blk_key->crypto_cfg.key_type == BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)
+ return qcom_ice_program_wrapped_key(ice, slot, blk_key);
+
+ if (ice->use_hwkm) {
+ dev_err_ratelimited(dev, "Got raw key when using HWKM\n");
+ return -EINVAL;
+ }
+
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);
@@ -200,14 +386,135 @@ int qcom_ice_program_key(struct qcom_ice *ice, unsigned int slot,
}
EXPORT_SYMBOL_GPL(qcom_ice_program_key);
int qcom_ice_evict_key(struct qcom_ice *ice, int slot)
{
+ if (ice->hwkm_init_complete)
+ slot = translate_hwkm_slot(ice, slot);
return qcom_scm_ice_invalidate_key(slot);
}
EXPORT_SYMBOL_GPL(qcom_ice_evict_key);
+/**
+ * qcom_ice_get_supported_key_type() - Get the supported key type
+ * @ice: ICE driver data
+ *
+ * Return: the blk-crypto key type that the ICE driver is configured to use.
+ * This is the key type that ICE-capable storage drivers should advertise as
+ * supported in the crypto capabilities of any disks they register.
+ */
+enum blk_crypto_key_type qcom_ice_get_supported_key_type(struct qcom_ice *ice)
+{
+ if (ice->use_hwkm)
+ return BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ return BLK_CRYPTO_KEY_TYPE_RAW;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_get_supported_key_type);
+
+/**
+ * qcom_ice_derive_sw_secret() - Derive software secret from wrapped key
+ * @ice: ICE driver data
+ * @eph_key: an ephemerally-wrapped key
+ * @eph_key_size: size of @eph_key in bytes
+ * @sw_secret: output buffer for the software secret
+ *
+ * Use HWKM to derive the "software secret" from a hardware-wrapped key that is
+ * given in ephemerally-wrapped form.
+ *
+ * Return: 0 on success; -EBADMSG if the given ephemerally-wrapped key is
+ * invalid; or another -errno value.
+ */
+int qcom_ice_derive_sw_secret(struct qcom_ice *ice,
+ const u8 *eph_key, size_t eph_key_size,
+ u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
+{
+ int err = qcom_scm_derive_sw_secret(eph_key, eph_key_size,
+ sw_secret,
+ BLK_CRYPTO_SW_SECRET_SIZE);
+ if (err == -EIO || err == -EINVAL)
+ err = -EBADMSG; /* probably invalid key */
+ return err;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret);
+
+/**
+ * qcom_ice_generate_key() - Generate a wrapped key for inline encryption
+ * @ice: ICE driver data
+ * @lt_key: output buffer for the long-term wrapped key
+ *
+ * Use HWKM to generate a new key and return it as a long-term wrapped key.
+ *
+ * Return: the size of the resulting wrapped key on success; -errno on failure.
+ */
+int qcom_ice_generate_key(struct qcom_ice *ice,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ int err;
+
+ err = qcom_scm_generate_ice_key(lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE);
+ if (err)
+ return err;
+
+ return QCOM_ICE_HWKM_WRAPPED_KEY_SIZE;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_generate_key);
+
+/**
+ * qcom_ice_prepare_key() - Prepare a wrapped key for inline encryption
+ * @ice: ICE driver data
+ * @lt_key: a long-term wrapped key
+ * @lt_key_size: size of @lt_key in bytes
+ * @eph_key: output buffer for the ephemerally-wrapped key
+ *
+ * Use HWKM to re-wrap a long-term wrapped key with the per-boot ephemeral key.
+ *
+ * Return: the size of the resulting wrapped key on success; -EBADMSG if the
+ * given long-term wrapped key is invalid; or another -errno value.
+ */
+int qcom_ice_prepare_key(struct qcom_ice *ice,
+ const u8 *lt_key, size_t lt_key_size,
+ u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ int err;
+
+ err = qcom_scm_prepare_ice_key(lt_key, lt_key_size,
+ eph_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE);
+ if (err == -EIO || err == -EINVAL)
+ err = -EBADMSG; /* probably invalid key */
+ if (err)
+ return err;
+
+ return QCOM_ICE_HWKM_WRAPPED_KEY_SIZE;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_prepare_key);
+
+/**
+ * qcom_ice_import_key() - Import a raw key for inline encryption
+ * @ice: ICE driver data
+ * @raw_key: the raw key to import
+ * @raw_key_size: size of @raw_key in bytes
+ * @lt_key: output buffer for the long-term wrapped key
+ *
+ * Use HWKM to import a raw key and return it as a long-term wrapped key.
+ *
+ * Return: the size of the resulting wrapped key on success; -errno on failure.
+ */
+int qcom_ice_import_key(struct qcom_ice *ice,
+ const u8 *raw_key, size_t raw_key_size,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+ int err;
+
+ err = qcom_scm_import_ice_key(raw_key, raw_key_size,
+ lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE);
+ if (err)
+ return err;
+
+ return QCOM_ICE_HWKM_WRAPPED_KEY_SIZE;
+}
+EXPORT_SYMBOL_GPL(qcom_ice_import_key);
+
static struct qcom_ice *qcom_ice_create(struct device *dev,
void __iomem *base)
{
struct qcom_ice *engine;
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 9330022e98ee..f34527fb02fb 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -132,10 +132,15 @@ 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));
diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h
index 4cecc7f088b4..c0e32afd7fb8 100644
--- a/include/soc/qcom/ice.h
+++ b/include/soc/qcom/ice.h
@@ -15,7 +15,19 @@ 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, unsigned int slot,
const struct blk_crypto_key *blk_key);
int qcom_ice_evict_key(struct qcom_ice *ice, int slot);
+enum blk_crypto_key_type qcom_ice_get_supported_key_type(struct qcom_ice *ice);
+int qcom_ice_derive_sw_secret(struct qcom_ice *ice,
+ const u8 *eph_key, size_t eph_key_size,
+ u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);
+int qcom_ice_generate_key(struct qcom_ice *ice,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
+int qcom_ice_prepare_key(struct qcom_ice *ice,
+ const u8 *lt_key, size_t lt_key_size,
+ u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
+int qcom_ice_import_key(struct qcom_ice *ice,
+ const u8 *raw_key, size_t raw_key_size,
+ u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);
struct qcom_ice *of_qcom_ice_get(struct device *dev);
#endif /* __QCOM_ICE_H__ */
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v12 3/4] ufs: qcom: add support for wrapped keys
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
2025-02-10 20:23 ` [PATCH v12 1/4] soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key Eric Biggers
2025-02-10 20:23 ` [PATCH v12 2/4] soc: qcom: ice: add HWKM support to the ICE driver Eric Biggers
@ 2025-02-10 20:23 ` Eric Biggers
2025-02-10 20:23 ` [PATCH v12 4/4] fscrypt: add support for hardware-wrapped keys Eric Biggers
2025-02-11 8:12 ` [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Bartosz Golaszewski
4 siblings, 0 replies; 8+ messages in thread
From: Eric Biggers @ 2025-02-10 20:23 UTC (permalink / raw)
To: linux-fscrypt, linux-scsi
Cc: linux-block, linux-mmc, linux-arm-msm, linux-kernel,
Bartosz Golaszewski, Gaurav Kashyap, Bjorn Andersson,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam, Bartosz Golaszewski
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.
Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Tested-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> # sm8650
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
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 f34527fb02fb..dc3eb6f29f5b 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
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v12 4/4] fscrypt: add support for hardware-wrapped keys
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
` (2 preceding siblings ...)
2025-02-10 20:23 ` [PATCH v12 3/4] ufs: qcom: add support for wrapped keys Eric Biggers
@ 2025-02-10 20:23 ` Eric Biggers
2025-02-11 8:12 ` [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Bartosz Golaszewski
4 siblings, 0 replies; 8+ messages in thread
From: Eric Biggers @ 2025-02-10 20:23 UTC (permalink / raw)
To: linux-fscrypt, linux-scsi
Cc: linux-block, linux-mmc, linux-arm-msm, linux-kernel,
Bartosz Golaszewski, Gaurav Kashyap, Bjorn Andersson,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam, Bartosz Golaszewski
From: Eric Biggers <ebiggers@google.com>
Add support for hardware-wrapped keys to fscrypt. 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. 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.
The version of this feature introduced by this patch is mostly
equivalent to the version that has existed downstream in the Android
Common Kernels since 2020. However, a couple fixes are included.
First, the flags field in struct fscrypt_add_key_arg is now placed in
the proper location. Second, key identifiers for HW-wrapped keys are
now derived using a distinct HKDF context byte; this fixes a bug where a
raw key could have the same identifier as a HW-wrapped key. Note that
as a result of these fixes, the version of this feature introduced by
this patch is not UAPI or on-disk format compatible with the version in
the Android Common Kernels, though the divergence is limited to just
those specific fixes. This version should be used going forwards.
This patch has been heavily rewritten from the original version by
Gaurav Kashyap <quic_gaurkash@quicinc.com> and
Barani Muthukumaran <bmuthuku@codeaurora.org>.
Tested-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> # sm8650
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
Documentation/filesystems/fscrypt.rst | 187 ++++++++++++++++++++------
fs/crypto/fscrypt_private.h | 75 +++++++++--
fs/crypto/hkdf.c | 4 +-
fs/crypto/inline_crypt.c | 44 +++++-
fs/crypto/keyring.c | 138 +++++++++++++------
fs/crypto/keysetup.c | 63 +++++++--
fs/crypto/keysetup_v1.c | 4 +-
include/uapi/linux/fscrypt.h | 6 +-
8 files changed, 413 insertions(+), 108 deletions(-)
diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 04eaab01314b..d3d808d0bff8 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -68,11 +68,11 @@ an authorized user later accessing the filesystem.
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
~~~~~~~~~~~~~~~~~~~~
fscrypt is only resistant to side-channel attacks, such as timing or
@@ -97,20 +97,27 @@ system itself, is *not* protected by the mathematical properties of
encryption but rather only by the correctness of the kernel.
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
encryption key from kernel memory. If it does so, it will also try to
evict all cached inodes which had been "unlocked" using the key,
@@ -143,10 +150,28 @@ However, these ioctls have some limitations:
- Secret keys might still exist in CPU registers, in crypto
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
~~~~~~~~~~~~~~~~~~~~~~~~~~
v1 encryption policies have some weaknesses with respect to online
attacks:
@@ -169,10 +194,14 @@ this reason among others, it is recommended to use v2 encryption
policies on all new encrypted directories.
Key hierarchy
=============
+Note: this section assumes the use of raw keys rather than
+hardware-wrapped keys. The use of hardware-wrapped keys modifies the
+key hierarchy slightly. For details, see `Hardware-wrapped keys`_.
+
Master Keys
-----------
Each encrypted directory tree is protected by a *master key*. Master
keys can be up to 64 bytes long, and must be at least as long as the
@@ -834,11 +863,13 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::
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[];
};
#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1
#define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2
@@ -853,11 +884,11 @@ a pointer to struct fscrypt_add_key_arg, defined as follows::
} u;
};
struct fscrypt_provisioning_key_payload {
__u32 type;
- __u32 __reserved;
+ __u32 flags;
__u8 raw[];
};
struct fscrypt_add_key_arg must be zeroed, then initialized
as follows:
@@ -881,28 +912,36 @@ as follows:
- ``raw_size`` must be the size of the ``raw`` key provided, in bytes.
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.
-- ``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
- struct fscrypt_provisioning_key_payload whose ``raw`` field contains
- the raw key and whose ``type`` field matches ``key_spec.type``.
- Since ``raw`` is variable-length, the total size of this key's
- payload must be ``sizeof(struct fscrypt_provisioning_key_payload)``
- plus the raw key size. The process must have Search permission on
- this key.
-
- Most users should leave this 0 and specify the raw key directly.
- The support for specifying a Linux keyring key is intended mainly to
+- ``key_id`` is 0 if the 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 struct
+ fscrypt_provisioning_key_payload whose ``raw`` field contains the
+ key, whose ``type`` field matches ``key_spec.type``, and whose
+ ``flags`` field matches ``flags``. Since ``raw`` is
+ variable-length, the total size of this key's payload must be
+ ``sizeof(struct fscrypt_provisioning_key_payload)`` plus the number
+ of key bytes. The process must have Search permission on this key.
+
+ Most users should leave this 0 and specify the key directly. The
+ support for specifying a Linux keyring key is intended mainly to
allow re-adding keys after a filesystem is unmounted and re-mounted,
- without having to store the raw keys in userspace memory.
+ without having to store the keys in userspace memory.
+
+- ``flags`` contains optional flags from ``<linux/fscrypt.h>``:
+
+ - 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.
- ``raw`` is a variable-length field which must contain the actual
key, ``raw_size`` bytes long. Alternatively, if ``key_id`` is
- nonzero, then this field is unused.
+ nonzero, then this field is unused. Note that despite being named
+ ``raw``, if FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED is specified then it
+ will contain a wrapped key, not a raw key.
For v2 policy keys, the kernel keeps track of which user (identified
by effective user ID) added the key, and only allows the key to be
removed by that user --- or by "root", if they use
`FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS`_.
@@ -910,34 +949,37 @@ removed by that user --- or by "root", if they use
However, if another user has added the key, it may be desirable to
prevent that other user from unexpectedly removing it. Therefore,
FS_IOC_ADD_ENCRYPTION_KEY may also be used to add a v2 policy key
*again*, even if it's already added by other user(s). In this case,
FS_IOC_ADD_ENCRYPTION_KEY will just install a claim to the key for the
-current user, rather than actually add the key again (but the raw key
-must still be provided, as a proof of knowledge).
+current user, rather than actually add the key again (but the key must
+still be provided, as a proof of knowledge).
FS_IOC_ADD_ENCRYPTION_KEY returns 0 if either the key or a claim to
the key was either added or already exists.
FS_IOC_ADD_ENCRYPTION_KEY can fail with the following errors:
- ``EACCES``: FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR was specified, but the
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
+ user namespace; or the key was specified by Linux key ID but the
process lacks Search permission on the key.
+- ``EBADMSG``: invalid 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
were set
-- ``EKEYREJECTED``: the raw key was specified by Linux key ID, but the
- key has the wrong type
-- ``ENOKEY``: the raw key was specified by Linux key ID, but no key
- exists with that ID
+- ``EKEYREJECTED``: the key was specified by Linux key ID, but the key
+ has the wrong type
+- ``ENOKEY``: the key was specified by Linux key ID, but no key exists
+ with that ID
- ``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 a hardware wrapped key was specified
+ but the filesystem does not support inline encryption or the hardware
+ does not support hardware-wrapped keys
Legacy method
~~~~~~~~~~~~~
For v1 encryption policies, a master encryption key can also be
@@ -996,13 +1038,12 @@ These two ioctls differ only in cases where v2 policy keys are added
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The FS_IOC_REMOVE_ENCRYPTION_KEY ioctl removes a claim to a master
@@ -1318,19 +1359,89 @@ encryption when possible; it doesn't force its use. fscrypt will
still fall back to using the kernel crypto API on files where the
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 raw 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
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; they are a
+block layer feature (part of *blk-crypto*). For more details about
+hardware-wrapped keys, see the block layer documentation at
+:ref:`Documentation/block/inline-encryption.rst
+<hardware_wrapped_keys>`. The rest of this section just focuses 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 raw 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. The key must be in ephemerally-wrapped form, not
+long-term wrapped form.
+
+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 raw 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.
Direct I/O support
==================
For direct I/O on an encrypted file to work, the following conditions
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 8371e4e1f596..c1d92074b65c 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -10,10 +10,11 @@
#ifndef _FSCRYPT_PRIVATE_H
#define _FSCRYPT_PRIVATE_H
#include <linux/fscrypt.h>
+#include <linux/minmax.h>
#include <linux/siphash.h>
#include <crypto/hash.h>
#include <linux/blk-crypto.h>
#define CONST_STRLEN(str) (sizeof(str) - 1)
@@ -25,10 +26,27 @@
* if ciphers with a 256-bit security strength are used. This is just the
* absolute minimum, which applies when only 128-bit encryption is used.
*/
#define FSCRYPT_MIN_KEY_SIZE 16
+/* Maximum size of a raw fscrypt master key */
+#define FSCRYPT_MAX_RAW_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 */
+#define FSCRYPT_MAX_ANY_KEY_SIZE \
+ MAX(FSCRYPT_MAX_RAW_KEY_SIZE, FSCRYPT_MAX_HW_WRAPPED_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 raw 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
/* Keep this in sync with include/uapi/linux/fscrypt.h */
#define FSCRYPT_MODE_MAX FSCRYPT_MODE_AES_256_HCTR2
@@ -358,41 +376,49 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
* the first byte of the HKDF application-specific info string to guarantee that
* info strings are never repeated between contexts. This ensures that all HKDF
* outputs are unique and cryptographically isolated, i.e. knowledge of one
* output doesn't reveal another.
*/
-#define HKDF_CONTEXT_KEY_IDENTIFIER 1 /* info=<empty> */
+#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY 1 /* info=<empty> */
#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=<empty> */
+#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \
+ 8 /* info=<empty> */
int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
const u8 *info, unsigned int infolen,
u8 *okm, unsigned int okmlen);
void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
/* inline_crypt.c */
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci);
+int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
+ bool is_hw_wrapped_key);
static inline bool
fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)
{
return ci->ci_inlinecrypt;
}
int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
- const u8 *raw_key,
+ const u8 *key_bytes, size_t key_size,
+ bool is_hw_wrapped,
const struct fscrypt_inode_info *ci);
void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key);
+int fscrypt_derive_sw_secret(struct super_block *sb,
+ const u8 *wrapped_key, size_t 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.
*/
static inline bool
@@ -412,11 +438,12 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
return smp_load_acquire(&prep_key->tfm) != NULL;
}
#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
-static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)
+static inline int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
+ bool is_hw_wrapped_key)
{
return 0;
}
static inline bool
@@ -425,11 +452,12 @@ fscrypt_using_inline_encryption(const struct fscrypt_inode_info *ci)
return false;
}
static inline int
fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
- const u8 *raw_key,
+ const u8 *key_bytes, size_t key_size,
+ bool is_hw_wrapped,
const struct fscrypt_inode_info *ci)
{
WARN_ON_ONCE(1);
return -EOPNOTSUPP;
}
@@ -438,10 +466,19 @@ static inline void
fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{
}
+static inline int
+fscrypt_derive_sw_secret(struct super_block *sb,
+ const u8 *wrapped_key, size_t 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_inode_info *ci)
{
return smp_load_acquire(&prep_key->tfm) != NULL;
@@ -454,24 +491,42 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
* fscrypt_master_key_secret - secret key material of an in-use master 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;
/*
- * Size of the raw key in bytes. This remains set even if ->raw was
+ * True if this key is a hardware-wrapped key; false if this key is a
+ * raw 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 key in bytes. This remains set even if ->bytes was
* zeroized due to no longer being needed. I.e. we still remember the
* size of the key even if we don't need to remember the key itself.
*/
u32 size;
- /* For v1 policy keys: the raw key. Wiped for v2 policy keys. */
- u8 raw[FSCRYPT_MAX_KEY_SIZE];
+ /*
+ * The bytes of the key, when still needed. This can be either a raw
+ * key or a hardware-wrapped key, as indicated by ->is_hw_wrapped. In
+ * the case of a raw, v2 policy key, there is no need to remember the
+ * actual key separately from ->hkdf so this field will be zeroized as
+ * soon as ->hkdf is initialized.
+ */
+ u8 bytes[FSCRYPT_MAX_ANY_KEY_SIZE];
} __randomize_layout;
/*
* fscrypt_master_key - an in-use master key
diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c
index 5a384dad2c72..7e007810e434 100644
--- a/fs/crypto/hkdf.c
+++ b/fs/crypto/hkdf.c
@@ -2,11 +2,13 @@
/*
* Implementation of HKDF ("HMAC-based Extract-and-Expand Key Derivation
* Function"), aka RFC 5869. See also the original paper (Krawczyk 2010):
* "Cryptographic Extraction and Key Derivation: The HKDF Scheme".
*
- * This is used to derive keys from the fscrypt master keys.
+ * 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
*/
#include <crypto/hash.h>
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 7fa53d30aec3..1d008c440cb6 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -87,11 +87,12 @@ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode,
}
}
}
/* Enable inline encryption for this file if supported. */
-int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)
+int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
+ bool is_hw_wrapped_key)
{
const struct inode *inode = ci->ci_inode;
struct super_block *sb = inode->i_sb;
struct blk_crypto_config crypto_cfg;
struct block_device **devs;
@@ -128,11 +129,12 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)
* crypto configuration that the file would use.
*/
crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;
crypto_cfg.data_unit_size = 1U << ci->ci_data_unit_bits;
crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci);
- crypto_cfg.key_type = BLK_CRYPTO_KEY_TYPE_RAW;
+ crypto_cfg.key_type = is_hw_wrapped_key ?
+ BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;
devs = fscrypt_get_devices(sb, &num_devs);
if (IS_ERR(devs))
return PTR_ERR(devs);
@@ -149,29 +151,31 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci)
return 0;
}
int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
- const u8 *raw_key,
+ const u8 *key_bytes, size_t key_size,
+ bool is_hw_wrapped,
const struct fscrypt_inode_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_RAW;
struct blk_crypto_key *blk_key;
struct block_device **devs;
unsigned int num_devs;
unsigned int i;
int err;
blk_key = kmalloc(sizeof(*blk_key), GFP_KERNEL);
if (!blk_key)
return -ENOMEM;
- err = blk_crypto_init_key(blk_key, raw_key, ci->ci_mode->keysize,
- BLK_CRYPTO_KEY_TYPE_RAW, crypto_mode,
- fscrypt_get_dun_bytes(ci),
+ err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type,
+ crypto_mode, fscrypt_get_dun_bytes(ci),
1U << ci->ci_data_unit_bits);
if (err) {
fscrypt_err(inode, "error %d initializing blk-crypto key", err);
goto fail;
}
@@ -226,10 +230,38 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
kfree(devs);
}
kfree_sensitive(blk_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, size_t wrapped_key_size,
+ u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
+{
+ 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;
+ }
+
+ err = blk_crypto_derive_sw_secret(sb->s_bdev, 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;
}
EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto);
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 787e9c8938ba..6399ee97ba62 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -147,15 +147,15 @@ static inline bool valid_key_spec(const struct fscrypt_key_specifier *spec)
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
+ * We just charge FSCRYPT_MAX_RAW_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_RAW_KEY_SIZE);
}
static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m)
{
seq_puts(m, key->description);
@@ -551,50 +551,92 @@ static int do_add_master_key(struct super_block *sb,
return err;
}
static int add_master_key(struct super_block *sb,
struct fscrypt_master_key_secret *secret,
- struct fscrypt_key_specifier *key_spec)
+ struct fscrypt_key_specifier *key_spec, u32 flags)
{
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->bytes;
+ unsigned int kdf_key_size = secret->size;
+ u8 keyid_kdf_ctx = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY;
/*
- * Now that the HKDF context is initialized, the raw key is no
- * longer needed.
+ * For raw 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->bytes,
+ 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 raw key, ensure
+ * that hardware-wrapped keys and raw 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)
return err;
}
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_RAW_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);
if (!prep->payload.data[0])
return -ENOMEM;
@@ -634,25 +676,25 @@ static struct key_type key_type_fscrypt_provisioning = {
.describe = fscrypt_provisioning_key_describe,
.destroy = fscrypt_provisioning_key_destroy,
};
/*
- * Retrieve the raw key from the Linux keyring key specified by 'key_id', and
- * store it into 'secret'.
+ * Retrieve the 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.
+ * re-mounted. Most users should just provide the 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;
struct key *key;
const struct fscrypt_provisioning_key_payload *payload;
@@ -665,16 +707,20 @@ static int get_keyring_key(u32 key_id, u32 type,
if (key->type != &key_type_fscrypt_provisioning)
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);
- memcpy(secret->raw, payload->raw, secret->size);
+ memcpy(secret->bytes, payload->raw, secret->size);
err = 0;
goto out_put;
bad_key:
err = -EKEYREJECTED;
@@ -732,27 +778,36 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
!capable(CAP_SYS_ADMIN))
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;
- if (copy_from_user(secret.raw, uarg->raw, secret.size))
+ if (copy_from_user(secret.bytes, uarg->raw, secret.size))
goto out_wipe_secret;
}
- err = add_master_key(sb, &secret, &arg.key_spec);
+ err = add_master_key(sb, &secret, &arg.key_spec, arg.flags);
if (err)
goto out_wipe_secret;
/* Return the key identifier to userspace, if applicable */
err = -EFAULT;
@@ -768,31 +823,32 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
static void
fscrypt_get_test_dummy_secret(struct fscrypt_master_key_secret *secret)
{
- static u8 test_key[FSCRYPT_MAX_KEY_SIZE];
+ static u8 test_key[FSCRYPT_MAX_RAW_KEY_SIZE];
- get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE);
+ get_random_once(test_key, sizeof(test_key));
memset(secret, 0, sizeof(*secret));
- secret->size = FSCRYPT_MAX_KEY_SIZE;
- memcpy(secret->raw, test_key, FSCRYPT_MAX_KEY_SIZE);
+ secret->size = sizeof(test_key);
+ memcpy(secret->bytes, test_key, sizeof(test_key));
}
int fscrypt_get_test_dummy_key_identifier(
u8 key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE])
{
struct fscrypt_master_key_secret secret;
int err;
fscrypt_get_test_dummy_secret(&secret);
- err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size);
+ err = fscrypt_init_hkdf(&secret.hkdf, secret.bytes, secret.size);
if (err)
goto out;
- err = fscrypt_hkdf_expand(&secret.hkdf, HKDF_CONTEXT_KEY_IDENTIFIER,
+ err = fscrypt_hkdf_expand(&secret.hkdf,
+ HKDF_CONTEXT_KEY_IDENTIFIER_FOR_RAW_KEY,
NULL, 0, key_identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE);
out:
wipe_master_key_secret(&secret);
return err;
@@ -815,11 +871,11 @@ int fscrypt_add_test_dummy_key(struct super_block *sb,
{
struct fscrypt_master_key_secret secret;
int err;
fscrypt_get_test_dummy_secret(&secret);
- err = add_master_key(sb, &secret, key_spec);
+ err = add_master_key(sb, &secret, key_spec, 0);
wipe_master_key_secret(&secret);
return err;
}
/*
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index b4fe01ea4bd4..0d71843af946 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -151,11 +151,13 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const struct fscrypt_inode_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,
+ ci->ci_mode->keysize,
+ false, ci);
tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
/*
@@ -193,18 +195,33 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
const struct inode *inode = ci->ci_inode;
const struct super_block *sb = inode->i_sb;
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_RAW_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_ONCE(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 capabilities are 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;
return 0;
}
@@ -212,10 +229,20 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
mutex_lock(&fscrypt_mode_key_setup_mutex);
if (fscrypt_is_key_prepared(prep_key, ci))
goto done_unlock;
+ if (use_hw_wrapped_key) {
+ err = fscrypt_prepare_inline_crypt_key(prep_key,
+ mk->mk_secret.bytes,
+ 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);
hkdf_info[hkdf_infolen++] = mode_num;
if (include_fs_uuid) {
@@ -334,10 +361,18 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
struct fscrypt_master_key *mk,
bool need_dirhash_key)
{
int err;
+ if (mk->mk_secret.is_hw_wrapped &&
+ !(ci->ci_policy.v2.flags & (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 |
+ FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))) {
+ fscrypt_warn(ci->ci_inode,
+ "Hardware-wrapped keys are only supported with IV_INO_LBLK policies");
+ return -EINVAL;
+ }
+
if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
/*
* DIRECT_KEY: instead of deriving per-file encryption keys, the
* per-file nonce will be included in all the IVs. But unlike
* v1 policies, for v2 policies in this case we don't encrypt
@@ -360,11 +395,11 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
true);
} else if (ci->ci_policy.v2.flags &
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_RAW_KEY_SIZE];
err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
HKDF_CONTEXT_PER_FILE_ENC_KEY,
ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
derived_key, ci->ci_mode->keysize);
@@ -443,14 +478,10 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
struct super_block *sb = ci->ci_inode->i_sb;
struct fscrypt_key_specifier mk_spec;
struct fscrypt_master_key *mk;
int err;
- err = fscrypt_select_encryption_impl(ci);
- if (err)
- return err;
-
err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
if (err)
return err;
mk = fscrypt_find_master_key(sb, &mk_spec);
@@ -474,10 +505,14 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
}
if (unlikely(!mk)) {
if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
return -ENOKEY;
+ err = fscrypt_select_encryption_impl(ci, false);
+ if (err)
+ return err;
+
/*
* As a legacy fallback for v1 policies, search for the key in
* the current task's subscribed keyrings too. Don't move this
* to before the search of ->s_master_keys, since users
* shouldn't be able to override filesystem-level keys.
@@ -495,13 +530,25 @@ static int setup_file_encryption_key(struct fscrypt_inode_info *ci,
if (!fscrypt_valid_master_key_size(mk, ci)) {
err = -ENOKEY;
goto out_release_key;
}
+ err = fscrypt_select_encryption_impl(ci, mk->mk_secret.is_hw_wrapped);
+ if (err)
+ goto out_release_key;
+
switch (ci->ci_policy.version) {
case FSCRYPT_POLICY_V1:
- err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
+ if (WARN_ON_ONCE(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.bytes);
break;
case FSCRYPT_POLICY_V2:
err = fscrypt_setup_v2_file_key(ci, mk, need_dirhash_key);
break;
default:
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index cf3b58ec32cc..b70521c55132 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -116,11 +116,11 @@ find_and_lock_process_key(const char *prefix,
goto invalid;
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 > sizeof(payload->raw)) {
fscrypt_warn(NULL,
"key with description '%s' has invalid payload",
key->description);
goto invalid;
}
@@ -147,11 +147,11 @@ struct fscrypt_direct_key {
struct hlist_node dk_node;
refcount_t dk_refcount;
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_RAW_KEY_SIZE];
};
static void free_direct_key(struct fscrypt_direct_key *dk)
{
if (dk) {
diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h
index 7a8f4c290187..3aff99f2696a 100644
--- a/include/uapi/linux/fscrypt.h
+++ b/include/uapi/linux/fscrypt.h
@@ -117,20 +117,22 @@ struct fscrypt_key_specifier {
* Payload of Linux keyring key of type "fscrypt-provisioning", referenced by
* fscrypt_add_key_arg::key_id as an alternative to fscrypt_add_key_arg::raw.
*/
struct fscrypt_provisioning_key_payload {
__u32 type;
- __u32 __reserved;
+ __u32 flags;
__u8 raw[];
};
/* Struct passed to FS_IOC_ADD_ENCRYPTION_KEY */
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[];
};
/* Struct passed to FS_IOC_REMOVE_ENCRYPTION_KEY */
struct fscrypt_remove_key_arg {
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
` (3 preceding siblings ...)
2025-02-10 20:23 ` [PATCH v12 4/4] fscrypt: add support for hardware-wrapped keys Eric Biggers
@ 2025-02-11 8:12 ` Bartosz Golaszewski
2025-03-02 22:23 ` Eric Biggers
4 siblings, 1 reply; 8+ messages in thread
From: Bartosz Golaszewski @ 2025-02-11 8:12 UTC (permalink / raw)
To: Eric Biggers
Cc: linux-fscrypt, linux-scsi, linux-block, linux-mmc, linux-arm-msm,
linux-kernel, Gaurav Kashyap, Bjorn Andersson, Dmitry Baryshkov,
Jens Axboe, Konrad Dybcio, Manivannan Sadhasivam
On Mon, Feb 10, 2025 at 9:25 PM Eric Biggers <ebiggers@kernel.org> wrote:
>
> This patchset is based on linux-block/for-next and is also available at:
>
> git fetch https://git.kernel.org/pub/scm/fs/fscrypt/linux.git wrapped-keys-v12
>
> Now that the block layer support for hardware-wrapped inline encryption
> keys has been applied for 6.15
> (https://lore.kernel.org/r/173920649542.40307.8847368467858129326.b4-ty@kernel.dk),
> this series refreshes the remaining patches. They add the support for
> hardware-wrapped inline encryption keys to the Qualcomm ICE and UFS
> drivers and to fscrypt. All tested on SM8650 with xfstests.
>
> TBD whether these will land in 6.15 too, or wait until 6.16 when the
> block patches that patches 2-4 depend on will have landed.
>
Could Jens provide an immutable branch with these patches? I don't
think there's a reason to delay it for another 3 months TBH.
Bartosz
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys
2025-02-11 8:12 ` [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Bartosz Golaszewski
@ 2025-03-02 22:23 ` Eric Biggers
2025-03-10 16:41 ` Eric Biggers
0 siblings, 1 reply; 8+ messages in thread
From: Eric Biggers @ 2025-03-02 22:23 UTC (permalink / raw)
To: Bartosz Golaszewski, Bjorn Andersson
Cc: linux-fscrypt, linux-scsi, linux-block, linux-mmc, linux-arm-msm,
linux-kernel, Gaurav Kashyap, Dmitry Baryshkov, Jens Axboe,
Konrad Dybcio, Manivannan Sadhasivam
On Tue, Feb 11, 2025 at 09:12:11AM +0100, Bartosz Golaszewski wrote:
> On Mon, Feb 10, 2025 at 9:25 PM Eric Biggers <ebiggers@kernel.org> wrote:
> >
> > This patchset is based on linux-block/for-next and is also available at:
> >
> > git fetch https://git.kernel.org/pub/scm/fs/fscrypt/linux.git wrapped-keys-v12
> >
> > Now that the block layer support for hardware-wrapped inline encryption
> > keys has been applied for 6.15
> > (https://lore.kernel.org/r/173920649542.40307.8847368467858129326.b4-ty@kernel.dk),
> > this series refreshes the remaining patches. They add the support for
> > hardware-wrapped inline encryption keys to the Qualcomm ICE and UFS
> > drivers and to fscrypt. All tested on SM8650 with xfstests.
> >
> > TBD whether these will land in 6.15 too, or wait until 6.16 when the
> > block patches that patches 2-4 depend on will have landed.
> >
>
> Could Jens provide an immutable branch with these patches? I don't
> think there's a reason to delay it for another 3 months TBH.
They don't seem to be on an immutable branch, so I'll just wait until the next
cycle, rather than trying to do something weird where I rebase the fscrypt tree
onto the block tree and also include driver patches. TBH, I've already been
waiting 5 years to land this, so an extra 9 weeks is not a big deal :-)
The first patch "soc: qcom: ice: make qcom_ice_program_key() take struct
blk_crypto_key" does not depend on the block ones though, and it could land in
6.15. Bjorn, would you like to take that patch through your tree to get it out
of the way?
- Eric
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys
2025-03-02 22:23 ` Eric Biggers
@ 2025-03-10 16:41 ` Eric Biggers
0 siblings, 0 replies; 8+ messages in thread
From: Eric Biggers @ 2025-03-10 16:41 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Bartosz Golaszewski, linux-fscrypt, linux-scsi, linux-block,
linux-mmc, linux-arm-msm, linux-kernel, Gaurav Kashyap,
Dmitry Baryshkov, Jens Axboe, Konrad Dybcio,
Manivannan Sadhasivam
On Sun, Mar 02, 2025 at 02:23:36PM -0800, Eric Biggers wrote:
> > > TBD whether these will land in 6.15 too, or wait until 6.16 when the
> > > block patches that patches 2-4 depend on will have landed.
> > >
> >
> > Could Jens provide an immutable branch with these patches? I don't
> > think there's a reason to delay it for another 3 months TBH.
>
> They don't seem to be on an immutable branch, so I'll just wait until the next
> cycle, rather than trying to do something weird where I rebase the fscrypt tree
> onto the block tree and also include driver patches. TBH, I've already been
> waiting 5 years to land this, so an extra 9 weeks is not a big deal :-)
>
> The first patch "soc: qcom: ice: make qcom_ice_program_key() take struct
> blk_crypto_key" does not depend on the block ones though, and it could land in
> 6.15. Bjorn, would you like to take that patch through your tree to get it out
> of the way?
Bjorn, could you apply patch 1 to your tree? Thanks!
- Eric
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-03-10 16:42 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-10 20:23 [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Eric Biggers
2025-02-10 20:23 ` [PATCH v12 1/4] soc: qcom: ice: make qcom_ice_program_key() take struct blk_crypto_key Eric Biggers
2025-02-10 20:23 ` [PATCH v12 2/4] soc: qcom: ice: add HWKM support to the ICE driver Eric Biggers
2025-02-10 20:23 ` [PATCH v12 3/4] ufs: qcom: add support for wrapped keys Eric Biggers
2025-02-10 20:23 ` [PATCH v12 4/4] fscrypt: add support for hardware-wrapped keys Eric Biggers
2025-02-11 8:12 ` [PATCH v12 0/4] Driver and fscrypt support for HW-wrapped inline encryption keys Bartosz Golaszewski
2025-03-02 22:23 ` Eric Biggers
2025-03-10 16:41 ` Eric Biggers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).