Re: [EXTERNAL] [PATCH v4 05/15] soc: qcom: ice: support for hardware wrapped keys

From: Kamlesh Gurudasani
Date: Sun Feb 04 2024 - 14:32:18 EST


Gaurav Kashyap <quic_gaurkash@xxxxxxxxxxx> writes:

> This message was sent from outside of Texas Instruments.
> Do not click links or open attachments unless you recognize the source of this email and know the content is safe.
>
> Now that HWKM support is added to ICE, extend the ICE
> driver to support hardware wrapped keys programming coming
> in from the storage controllers (ufs and emmc). This is
> similar to standard keys where the call is forwarded to
> Trustzone, however certain wrapped key and HWKM specific
> actions has to be performed around the SCM calls.
>
> Derive software secret support is also added by forwarding the
> call to the corresponding scm api.
>
> Signed-off-by: Gaurav Kashyap <quic_gaurkash@xxxxxxxxxxx>
> Tested-by: Neil Armstrong <neil.armstrong@xxxxxxxxxx>
> ---
> drivers/soc/qcom/ice.c | 119 +++++++++++++++++++++++++++++++++++++----
> include/soc/qcom/ice.h | 4 ++
> 2 files changed, 112 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
> index c718e8153b23..c3b852269dca 100644
> --- a/drivers/soc/qcom/ice.c
> +++ b/drivers/soc/qcom/ice.c
> @@ -27,6 +27,8 @@
> #define QCOM_ICE_REG_BIST_STATUS 0x0070
> #define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
> #define QCOM_ICE_REG_CONTROL 0x0
> +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 0x4040
> +
> /* QCOM ICE HWKM registers */
> #define QCOM_ICE_REG_HWKM_TZ_KM_CTL 0x1000
> #define QCOM_ICE_REG_HWKM_TZ_KM_STATUS 0x1004
> @@ -48,6 +50,8 @@
> #define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
> #define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
>
> +#define QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET 0x80
> +
> #define QCOM_ICE_HWKM_REG_OFFSET 0x8000
> #define HWKM_OFFSET(reg) ((reg) + QCOM_ICE_HWKM_REG_OFFSET)
>
> @@ -68,6 +72,16 @@ struct qcom_ice {
> bool hwkm_init_complete;
> };
>
> +union crypto_cfg {
> + __le32 regval;
> + struct {
> + u8 dusize;
> + u8 capidx;
> + u8 reserved;
> + u8 cfge;
> + };
> +};
> +
> static bool qcom_ice_check_supported(struct qcom_ice *ice)
> {
> u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION);
> @@ -273,6 +287,51 @@ int qcom_ice_suspend(struct qcom_ice *ice)
> }
> EXPORT_SYMBOL_GPL(qcom_ice_suspend);
>
> +/*
> + * HW dictates the internal mapping between the ICE and HWKM slots,
> + * which are different for different versions, make the translation
> + * here. For v1 however, the translation is done in trustzone.
> + */
> +static int translate_hwkm_slot(struct qcom_ice *ice, int slot)
> +{
> + return (ice->hwkm_version == 1) ? slot : (slot * 2);
> +}
> +
> +static int qcom_ice_program_wrapped_key(struct qcom_ice *ice,
> + const struct blk_crypto_key *key,
> + u8 data_unit_size, int slot)
> +{
> + union crypto_cfg cfg;
> + int hwkm_slot;
> + int err;
> +
> + hwkm_slot = translate_hwkm_slot(ice, slot);
> +
> + memset(&cfg, 0, sizeof(cfg));
> + cfg.dusize = data_unit_size;
> + cfg.capidx = QCOM_SCM_ICE_CIPHER_AES_256_XTS;
> + cfg.cfge = 0x80;
> +
This is a clever use of union, writing from one member and
reading from inactive member.

I hope this a common practice in linux(being used in some other module
already) or fits the standard of C or will give deterministic result on
other compilers as well. Thanks.

Regards,
Kamlesh

> + /* Clear CFGE */
> + qcom_ice_writel(ice, 0x0, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 +
> + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot);
> +
> + /* Call trustzone to program the wrapped key using hwkm */
> + err = qcom_scm_ice_set_key(hwkm_slot, key->raw, key->size,
> + QCOM_SCM_ICE_CIPHER_AES_256_XTS, data_unit_size);
> + if (err) {
> + pr_err("%s:SCM call Error: 0x%x slot %d\n", __func__, err,
> + slot);
> + return err;
> + }
> +
> + /* Enable CFGE after programming key */
> + qcom_ice_writel(ice, cfg.regval, QCOM_ICE_LUT_KEYS_CRYPTOCFG_R16 +
> + QCOM_ICE_LUT_KEYS_CRYPTOCFG_OFFSET * slot);
> +
> + return err;
> +}
> +
> int qcom_ice_program_key(struct qcom_ice *ice,
> u8 algorithm_id, u8 key_size,
> const struct blk_crypto_key *bkey,
> @@ -288,24 +347,39 @@ int qcom_ice_program_key(struct qcom_ice *ice,
>
> /* 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) {
> + (key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256 &&
> + key_size != QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED)) {
> dev_err_ratelimited(dev,
> "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
> algorithm_id, key_size);
> return -EINVAL;
> }
>
> - memcpy(key.bytes, bkey->raw, 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]);
> + if (bkey->crypto_cfg.key_type == BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) {
> + /* It is expected that HWKM init has completed before programming wrapped keys */
> + if (!ice->use_hwkm || !ice->hwkm_init_complete) {
> + dev_err_ratelimited(dev, "HWKM not currently used or initialized\n");
> + return -EINVAL;
> + }
> + err = qcom_ice_program_wrapped_key(ice, bkey, data_unit_size,
> + slot);
> + } else {
> + if (bkey->size != QCOM_ICE_CRYPTO_KEY_SIZE_256)
> + dev_err_ratelimited(dev,
> + "Incorrect key size; bkey->size=%d\n",
> + algorithm_id);
> + return -EINVAL;
> + memcpy(key.bytes, bkey->raw, AES_256_XTS_KEY_SIZE);
>
> - err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
> - QCOM_SCM_ICE_CIPHER_AES_256_XTS,
> - data_unit_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]);
>
> - memzero_explicit(&key, sizeof(key));
> + err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
> + QCOM_SCM_ICE_CIPHER_AES_256_XTS,
> + data_unit_size);
> + memzero_explicit(&key, sizeof(key));
> + }
>
> return err;
> }
> @@ -313,7 +387,21 @@ EXPORT_SYMBOL_GPL(qcom_ice_program_key);
>
> int qcom_ice_evict_key(struct qcom_ice *ice, int slot)
> {
> - return qcom_scm_ice_invalidate_key(slot);
> + int hwkm_slot = slot;
> +
> + if (ice->use_hwkm) {
> + hwkm_slot = translate_hwkm_slot(ice, slot);
> + /*
> + * Ignore calls to evict key when HWKM is supported and hwkm init
> + * is not yet done. This is to avoid the clearing all slots call
> + * during a storage reset when ICE is still in legacy mode. HWKM slave
> + * in ICE takes care of zeroing out the keytable on reset.
> + */
> + if (!ice->hwkm_init_complete)
> + return 0;
> + }
> +
> + return qcom_scm_ice_invalidate_key(hwkm_slot);
> }
> EXPORT_SYMBOL_GPL(qcom_ice_evict_key);
>
> @@ -323,6 +411,15 @@ bool qcom_ice_hwkm_supported(struct qcom_ice *ice)
> }
> EXPORT_SYMBOL_GPL(qcom_ice_hwkm_supported);
>
> +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[],
> + unsigned int wkey_size,
> + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
> +{
> + return qcom_scm_derive_sw_secret(wkey, wkey_size,
> + sw_secret, BLK_CRYPTO_SW_SECRET_SIZE);
> +}
> +EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret);
> +
> static struct qcom_ice *qcom_ice_create(struct device *dev,
> void __iomem *base)
> {
> diff --git a/include/soc/qcom/ice.h b/include/soc/qcom/ice.h
> index 1f52e82e3e1c..dabe0d3a1fd0 100644
> --- a/include/soc/qcom/ice.h
> +++ b/include/soc/qcom/ice.h
> @@ -17,6 +17,7 @@ enum qcom_ice_crypto_key_size {
> QCOM_ICE_CRYPTO_KEY_SIZE_192 = 0x2,
> QCOM_ICE_CRYPTO_KEY_SIZE_256 = 0x3,
> QCOM_ICE_CRYPTO_KEY_SIZE_512 = 0x4,
> + QCOM_ICE_CRYPTO_KEY_SIZE_WRAPPED = 0x5,
> };
>
> enum qcom_ice_crypto_alg {
> @@ -35,5 +36,8 @@ int qcom_ice_program_key(struct qcom_ice *ice,
> u8 data_unit_size, int slot);
> int qcom_ice_evict_key(struct qcom_ice *ice, int slot);
> bool qcom_ice_hwkm_supported(struct qcom_ice *ice);
> +int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 wkey[],
> + unsigned int wkey_size,
> + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);
> struct qcom_ice *of_qcom_ice_get(struct device *dev);
> #endif /* __QCOM_ICE_H__ */
> --
> 2.43.0