diff mbox series

[v3,02/12] qcom_scm: scm call for deriving a software secret

Message ID 20231122053817.3401748-3-quic_gaurkash@quicinc.com
State New
Headers show
Series Hardware wrapped key support for qcom ice and ufs | expand

Commit Message

Gaurav Kashyap (QUIC) Nov. 22, 2023, 5:38 a.m. UTC
Inline storage encryption requires deriving a sw secret from
the hardware wrapped keys. For non-wrapped keys, this can be
directly done as keys are in the clear.

However, when keys are hardware wrapped, it can be unwrapped
by HWKM (Hardware Key Manager) which is accessible only from Qualcomm
Trustzone. Hence, it also makes sense that the software secret is also
derived there and returned to the linux kernel . This can be invoked by
using the crypto profile APIs provided by the block layer.

Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com>
---
 drivers/firmware/qcom/qcom_scm.c       | 71 ++++++++++++++++++++++++++
 drivers/firmware/qcom/qcom_scm.h       |  1 +
 include/linux/firmware/qcom/qcom_scm.h |  2 +
 3 files changed, 74 insertions(+)

Comments

Om Prakash Singh Dec. 8, 2023, 6:38 a.m. UTC | #1
On 11/22/2023 11:08 AM, Gaurav Kashyap wrote:
> Inline storage encryption requires deriving a sw secret from
> the hardware wrapped keys. For non-wrapped keys, this can be
> directly done as keys are in the clear.
> 
> However, when keys are hardware wrapped, it can be unwrapped
> by HWKM (Hardware Key Manager) which is accessible only from Qualcomm
> Trustzone. Hence, it also makes sense that the software secret is also
> derived there and returned to the linux kernel . This can be invoked by
> using the crypto profile APIs provided by the block layer.
> 
> Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com>
> ---
>   drivers/firmware/qcom/qcom_scm.c       | 71 ++++++++++++++++++++++++++
>   drivers/firmware/qcom/qcom_scm.h       |  1 +
>   include/linux/firmware/qcom/qcom_scm.h |  2 +
>   3 files changed, 74 insertions(+)
> 
> diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
> index 520de9b5633a..6dfb913f3e33 100644
> --- a/drivers/firmware/qcom/qcom_scm.c
> +++ b/drivers/firmware/qcom/qcom_scm.c
> @@ -1214,6 +1214,77 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
>   }
>   EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
>   
> +/**
> + * qcom_scm_derive_sw_secret() - Derive software secret from wrapped key
> + * @wkey: the hardware wrapped key inaccessible to software
> + * @wkey_size: size of the wrapped key
> + * @sw_secret: the secret to be derived which is exactly the secret size
> + * @sw_secret_size: size of the sw_secret
> + *
> + * Derive a software secret from a hardware wrapped key for software crypto
> + * operations.
> + * For wrapped keys, the key needs to be unwrapped, in order to derive a
> + * software secret, which can be done in the hardware from a secure execution
> + * environment.
> + *
> + * For more information on sw secret, please refer to "Hardware-wrapped keys"
> + * section of Documentation/block/inline-encryption.rst.
> + *
> + * Return: 0 on success; -errno on failure.
> + */
> +int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
> +			      u8 *sw_secret, size_t sw_secret_size)
> +{
> +	struct qcom_scm_desc desc = {
> +		.svc = QCOM_SCM_SVC_ES,
> +		.cmd =  QCOM_SCM_ES_DERIVE_SW_SECRET,
> +		.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW,
> +					 QCOM_SCM_VAL, QCOM_SCM_RW,
> +					 QCOM_SCM_VAL),
> +		.args[1] = wkey_size,
> +		.args[3] = sw_secret_size,
> +		.owner = ARM_SMCCC_OWNER_SIP,
> +	};
> +
> +	void *wkey_buf, *secret_buf;
> +	dma_addr_t wkey_phys, secret_phys;
> +	int ret;
> +
> +	/*
> +	 * Like qcom_scm_ice_set_key(), we use dma_alloc_coherent() to properly
> +	 * get a physical address, while guaranteeing that we can zeroize the
> +	 * key material later using memzero_explicit().
> +	 */
> +	wkey_buf = dma_alloc_coherent(__scm->dev, wkey_size, &wkey_phys, GFP_KERNEL);
> +	if (!wkey_buf)
> +		return -ENOMEM;
> +	secret_buf = dma_alloc_coherent(__scm->dev, sw_secret_size, &secret_phys, GFP_KERNEL);
> +	if (!secret_buf) {
> +		ret = -ENOMEM;
> +		goto err_free_wrapped;
> +	}
> +
> +	memcpy(wkey_buf, wkey, wkey_size);
> +	desc.args[0] = wkey_phys;
> +	desc.args[2] = secret_phys;
> +
> +	ret = qcom_scm_call(__scm->dev, &desc, NULL);
> +	if (!ret)
> +		memcpy(sw_secret, secret_buf, sw_secret_size);
> +
> +	memzero_explicit(secret_buf, sw_secret_size);
> +
> +	dma_free_coherent(__scm->dev, sw_secret_size, secret_buf, secret_phys);
> +
> +err_free_wrapped:
> +	memzero_explicit(wkey_buf, wkey_size);
In error handling case the operation is being performed on unallocated 
memory.
> +
> +	dma_free_coherent(__scm->dev, wkey_size, wkey_buf, wkey_phys);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(qcom_scm_derive_sw_secret);
> +
>   /**
>    * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
>    *
> diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
> index 4532907e8489..c75456aa6ac5 100644
> --- a/drivers/firmware/qcom/qcom_scm.h
> +++ b/drivers/firmware/qcom/qcom_scm.h
> @@ -121,6 +121,7 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
>   #define QCOM_SCM_SVC_ES			0x10	/* Enterprise Security */
>   #define QCOM_SCM_ES_INVALIDATE_ICE_KEY	0x03
>   #define QCOM_SCM_ES_CONFIG_SET_ICE_KEY	0x04
> +#define QCOM_SCM_ES_DERIVE_SW_SECRET	0x07
>   
>   #define QCOM_SCM_SVC_HDCP		0x11
>   #define QCOM_SCM_HDCP_INVOKE		0x01
> diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
> index ccaf28846054..c65f2d61492d 100644
> --- a/include/linux/firmware/qcom/qcom_scm.h
> +++ b/include/linux/firmware/qcom/qcom_scm.h
> @@ -103,6 +103,8 @@ bool qcom_scm_ice_available(void);
>   int qcom_scm_ice_invalidate_key(u32 index);
>   int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
>   			 enum qcom_scm_ice_cipher cipher, u32 data_unit_size);
> +int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
> +			      u8 *sw_secret, size_t sw_secret_size);
>   
>   bool qcom_scm_hdcp_available(void);
>   int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
Gaurav Kashyap Dec. 12, 2023, 4:09 a.m. UTC | #2
Hello Om,

On 12/07/2023, Om Prakash Singh wrote:
> On 11/22/2023 11:08 AM, Gaurav Kashyap wrote:
> > Inline storage encryption requires deriving a sw secret from the
> > hardware wrapped keys. For non-wrapped keys, this can be directly done
> > as keys are in the clear.
> >
> > However, when keys are hardware wrapped, it can be unwrapped by
> HWKM
> > (Hardware Key Manager) which is accessible only from Qualcomm
> > Trustzone. Hence, it also makes sense that the software secret is also
> > derived there and returned to the linux kernel . This can be invoked
> > by using the crypto profile APIs provided by the block layer.
> >
> > Signed-off-by: Gaurav Kashyap <quic_gaurkash@quicinc.com>
> > ---
> >   drivers/firmware/qcom/qcom_scm.c       | 71
> ++++++++++++++++++++++++++
> >   drivers/firmware/qcom/qcom_scm.h       |  1 +
> >   include/linux/firmware/qcom/qcom_scm.h |  2 +
> >   3 files changed, 74 insertions(+)
> >
> > diff --git a/drivers/firmware/qcom/qcom_scm.c
> > b/drivers/firmware/qcom/qcom_scm.c
> > index 520de9b5633a..6dfb913f3e33 100644
> > --- a/drivers/firmware/qcom/qcom_scm.c
> > +++ b/drivers/firmware/qcom/qcom_scm.c
> > @@ -1214,6 +1214,77 @@ int qcom_scm_ice_set_key(u32 index, const u8
> *key, u32 key_size,
> >   }
> >   EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
> >
> > +/**
> > + * qcom_scm_derive_sw_secret() - Derive software secret from wrapped
> > +key
> > + * @wkey: the hardware wrapped key inaccessible to software
> > + * @wkey_size: size of the wrapped key
> > + * @sw_secret: the secret to be derived which is exactly the secret
> > +size
> > + * @sw_secret_size: size of the sw_secret
> > + *
> > + * Derive a software secret from a hardware wrapped key for software
> > +crypto
> > + * operations.
> > + * For wrapped keys, the key needs to be unwrapped, in order to
> > +derive a
> > + * software secret, which can be done in the hardware from a secure
> > +execution
> > + * environment.
> > + *
> > + * For more information on sw secret, please refer to "Hardware-wrapped
> keys"
> > + * section of Documentation/block/inline-encryption.rst.
> > + *
> > + * Return: 0 on success; -errno on failure.
> > + */
> > +int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
> > +			      u8 *sw_secret, size_t sw_secret_size) {
> > +	struct qcom_scm_desc desc = {
> > +		.svc = QCOM_SCM_SVC_ES,
> > +		.cmd =  QCOM_SCM_ES_DERIVE_SW_SECRET,
> > +		.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW,
> > +					 QCOM_SCM_VAL, QCOM_SCM_RW,
> > +					 QCOM_SCM_VAL),
> > +		.args[1] = wkey_size,
> > +		.args[3] = sw_secret_size,
> > +		.owner = ARM_SMCCC_OWNER_SIP,
> > +	};
> > +
> > +	void *wkey_buf, *secret_buf;
> > +	dma_addr_t wkey_phys, secret_phys;
> > +	int ret;
> > +
> > +	/*
> > +	 * Like qcom_scm_ice_set_key(), we use dma_alloc_coherent() to
> properly
> > +	 * get a physical address, while guaranteeing that we can zeroize the
> > +	 * key material later using memzero_explicit().
> > +	 */
> > +	wkey_buf = dma_alloc_coherent(__scm->dev, wkey_size,
> &wkey_phys, GFP_KERNEL);
> > +	if (!wkey_buf)
> > +		return -ENOMEM;
> > +	secret_buf = dma_alloc_coherent(__scm->dev, sw_secret_size,
> &secret_phys, GFP_KERNEL);
> > +	if (!secret_buf) {
> > +		ret = -ENOMEM;
> > +		goto err_free_wrapped;
> > +	}
> > +
> > +	memcpy(wkey_buf, wkey, wkey_size);
> > +	desc.args[0] = wkey_phys;
> > +	desc.args[2] = secret_phys;
> > +
> > +	ret = qcom_scm_call(__scm->dev, &desc, NULL);
> > +	if (!ret)
> > +		memcpy(sw_secret, secret_buf, sw_secret_size);
> > +
> > +	memzero_explicit(secret_buf, sw_secret_size);
> > +
> > +	dma_free_coherent(__scm->dev, sw_secret_size, secret_buf,
> > +secret_phys);
> > +
> > +err_free_wrapped:
> > +	memzero_explicit(wkey_buf, wkey_size);
> In error handling case the operation is being performed on unallocated
> memory.

The flow comes here only when secret_buf allocation fails.
And at that point, wkey_buf is already allocated.
Wkey_buf failure errors out directly after allocation failure.

> > +
> > +	dma_free_coherent(__scm->dev, wkey_size, wkey_buf,
> wkey_phys);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(qcom_scm_derive_sw_secret);
> > +
> >   /**
> >    * qcom_scm_hdcp_available() - Check if secure environment supports
> HDCP.
> >    *
> > diff --git a/drivers/firmware/qcom/qcom_scm.h
> > b/drivers/firmware/qcom/qcom_scm.h
> > index 4532907e8489..c75456aa6ac5 100644
> > --- a/drivers/firmware/qcom/qcom_scm.h
> > +++ b/drivers/firmware/qcom/qcom_scm.h
> > @@ -121,6 +121,7 @@ int scm_legacy_call(struct device *dev, const struct
> qcom_scm_desc *desc,
> >   #define QCOM_SCM_SVC_ES			0x10	/* Enterprise
> Security */
> >   #define QCOM_SCM_ES_INVALIDATE_ICE_KEY	0x03
> >   #define QCOM_SCM_ES_CONFIG_SET_ICE_KEY	0x04
> > +#define QCOM_SCM_ES_DERIVE_SW_SECRET	0x07
> >
> >   #define QCOM_SCM_SVC_HDCP		0x11
> >   #define QCOM_SCM_HDCP_INVOKE		0x01
> > diff --git a/include/linux/firmware/qcom/qcom_scm.h
> > b/include/linux/firmware/qcom/qcom_scm.h
> > index ccaf28846054..c65f2d61492d 100644
> > --- a/include/linux/firmware/qcom/qcom_scm.h
> > +++ b/include/linux/firmware/qcom/qcom_scm.h
> > @@ -103,6 +103,8 @@ bool qcom_scm_ice_available(void);
> >   int qcom_scm_ice_invalidate_key(u32 index);
> >   int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
> >   			 enum qcom_scm_ice_cipher cipher, u32
> data_unit_size);
> > +int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
> > +			      u8 *sw_secret, size_t sw_secret_size);
> >
> >   bool qcom_scm_hdcp_available(void);
> >   int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
> > u32 *resp);
diff mbox series

Patch

diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 520de9b5633a..6dfb913f3e33 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -1214,6 +1214,77 @@  int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
 }
 EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
 
+/**
+ * qcom_scm_derive_sw_secret() - Derive software secret from wrapped key
+ * @wkey: the hardware wrapped key inaccessible to software
+ * @wkey_size: size of the wrapped key
+ * @sw_secret: the secret to be derived which is exactly the secret size
+ * @sw_secret_size: size of the sw_secret
+ *
+ * Derive a software secret from a hardware wrapped key for software crypto
+ * operations.
+ * For wrapped keys, the key needs to be unwrapped, in order to derive a
+ * software secret, which can be done in the hardware from a secure execution
+ * environment.
+ *
+ * For more information on sw secret, please refer to "Hardware-wrapped keys"
+ * section of Documentation/block/inline-encryption.rst.
+ *
+ * Return: 0 on success; -errno on failure.
+ */
+int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
+			      u8 *sw_secret, size_t sw_secret_size)
+{
+	struct qcom_scm_desc desc = {
+		.svc = QCOM_SCM_SVC_ES,
+		.cmd =  QCOM_SCM_ES_DERIVE_SW_SECRET,
+		.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW,
+					 QCOM_SCM_VAL, QCOM_SCM_RW,
+					 QCOM_SCM_VAL),
+		.args[1] = wkey_size,
+		.args[3] = sw_secret_size,
+		.owner = ARM_SMCCC_OWNER_SIP,
+	};
+
+	void *wkey_buf, *secret_buf;
+	dma_addr_t wkey_phys, secret_phys;
+	int ret;
+
+	/*
+	 * Like qcom_scm_ice_set_key(), we use dma_alloc_coherent() to properly
+	 * get a physical address, while guaranteeing that we can zeroize the
+	 * key material later using memzero_explicit().
+	 */
+	wkey_buf = dma_alloc_coherent(__scm->dev, wkey_size, &wkey_phys, GFP_KERNEL);
+	if (!wkey_buf)
+		return -ENOMEM;
+	secret_buf = dma_alloc_coherent(__scm->dev, sw_secret_size, &secret_phys, GFP_KERNEL);
+	if (!secret_buf) {
+		ret = -ENOMEM;
+		goto err_free_wrapped;
+	}
+
+	memcpy(wkey_buf, wkey, wkey_size);
+	desc.args[0] = wkey_phys;
+	desc.args[2] = secret_phys;
+
+	ret = qcom_scm_call(__scm->dev, &desc, NULL);
+	if (!ret)
+		memcpy(sw_secret, secret_buf, sw_secret_size);
+
+	memzero_explicit(secret_buf, sw_secret_size);
+
+	dma_free_coherent(__scm->dev, sw_secret_size, secret_buf, secret_phys);
+
+err_free_wrapped:
+	memzero_explicit(wkey_buf, wkey_size);
+
+	dma_free_coherent(__scm->dev, wkey_size, wkey_buf, wkey_phys);
+
+	return ret;
+}
+EXPORT_SYMBOL(qcom_scm_derive_sw_secret);
+
 /**
  * qcom_scm_hdcp_available() - Check if secure environment supports HDCP.
  *
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index 4532907e8489..c75456aa6ac5 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -121,6 +121,7 @@  int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
 #define QCOM_SCM_SVC_ES			0x10	/* Enterprise Security */
 #define QCOM_SCM_ES_INVALIDATE_ICE_KEY	0x03
 #define QCOM_SCM_ES_CONFIG_SET_ICE_KEY	0x04
+#define QCOM_SCM_ES_DERIVE_SW_SECRET	0x07
 
 #define QCOM_SCM_SVC_HDCP		0x11
 #define QCOM_SCM_HDCP_INVOKE		0x01
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index ccaf28846054..c65f2d61492d 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -103,6 +103,8 @@  bool qcom_scm_ice_available(void);
 int qcom_scm_ice_invalidate_key(u32 index);
 int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
 			 enum qcom_scm_ice_cipher cipher, u32 data_unit_size);
+int qcom_scm_derive_sw_secret(const u8 *wkey, size_t wkey_size,
+			      u8 *sw_secret, size_t sw_secret_size);
 
 bool qcom_scm_hdcp_available(void);
 int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);