[PATCH mvebu 1/2] firmware: turris-mox-rwtm: support ECDSA signatures via debugfs

Gregory CLEMENT gregory.clement at bootlin.com
Sat Jul 18 16:36:16 EDT 2020


Marek Behún <marek.behun at nic.cz> writes:

> The firmware on Turris MOX secure processor offers signing messages
> with ECDSA private key stored in protected OTP memory.
>
> The optimal solution would be to register an akcipher provider via
> kernel's crypto API, but crypto API does not yet support accessing
> akcipher API from userspace (and probably won't for some time, see
> https://www.spinics.net/lists/linux-crypto/msg38388.html).
>
> At first I tried to put this via standard sysfs API, but the way I
> designed it is not compatible with sysfs's standard "one file per
> attribute".
>
> This patch therefore adds support for accessing this signature
> generation mechanism via debugfs. Since CZ.NIC's Turris MOX is the only
> user of this module, the potential future change to akcipher API should
> not cause problems, since we can just change our userspace software then.
>
> Signed-off-by: Marek Behún <marek.behun at nic.cz>

Applied on mvebu/drivers

Thanks,

Gregory
> ---
>  drivers/firmware/turris-mox-rwtm.c | 166 +++++++++++++++++++++++++++++
>  1 file changed, 166 insertions(+)
>
> diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c
> index e27f68437b56..50bb2a6d6ccf 100644
> --- a/drivers/firmware/turris-mox-rwtm.c
> +++ b/drivers/firmware/turris-mox-rwtm.c
> @@ -7,6 +7,7 @@
>  
>  #include <linux/armada-37xx-rwtm-mailbox.h>
>  #include <linux/completion.h>
> +#include <linux/debugfs.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/hw_random.h>
>  #include <linux/mailbox_client.h>
> @@ -69,6 +70,18 @@ struct mox_rwtm {
>  	/* public key burned in eFuse */
>  	int has_pubkey;
>  	u8 pubkey[135];
> +
> +#ifdef CONFIG_DEBUG_FS
> +	/*
> +	 * Signature process. This is currently done via debugfs, because it
> +	 * does not conform to the sysfs standard "one file per attribute".
> +	 * It should be rewritten via crypto API once akcipher API is available
> +	 * from userspace.
> +	 */
> +	struct dentry *debugfs_root;
> +	u32 last_sig[34];
> +	int last_sig_done;
> +#endif
>  };
>  
>  struct mox_kobject {
> @@ -279,6 +292,152 @@ static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
>  	return ret;
>  }
>  
> +#ifdef CONFIG_DEBUG_FS
> +static int rwtm_debug_open(struct inode *inode, struct file *file)
> +{
> +	file->private_data = inode->i_private;
> +
> +	return nonseekable_open(inode, file);
> +}
> +
> +static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
> +			    loff_t *ppos)
> +{
> +	struct mox_rwtm *rwtm = file->private_data;
> +	ssize_t ret;
> +
> +	/* only allow one read, of 136 bytes, from position 0 */
> +	if (*ppos != 0)
> +		return 0;
> +
> +	if (len < 136)
> +		return -EINVAL;
> +
> +	if (!rwtm->last_sig_done)
> +		return -ENODATA;
> +
> +	/* 2 arrays of 17 32-bit words are 136 bytes */
> +	ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, 136);
> +	rwtm->last_sig_done = 0;
> +
> +	return ret;
> +}
> +
> +static ssize_t do_sign_write(struct file *file, const char __user *buf,
> +			     size_t len, loff_t *ppos)
> +{
> +	struct mox_rwtm *rwtm = file->private_data;
> +	struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
> +	struct armada_37xx_rwtm_tx_msg msg;
> +	loff_t dummy = 0;
> +	ssize_t ret;
> +
> +	/* the input is a SHA-512 hash, so exactly 64 bytes have to be read */
> +	if (len != 64)
> +		return -EINVAL;
> +
> +	/* if last result is not zero user has not read that information yet */
> +	if (rwtm->last_sig_done)
> +		return -EBUSY;
> +
> +	if (!mutex_trylock(&rwtm->busy))
> +		return -EBUSY;
> +
> +	/*
> +	 * Here we have to send:
> +	 *   1. Address of the input to sign.
> +	 *      The input is an array of 17 32-bit words, the first (most
> +	 *      significat) is 0, the rest 16 words are copied from the SHA-512
> +	 *      hash given by the user and converted from BE to LE.
> +	 *   2. Address of the buffer where ECDSA signature value R shall be
> +	 *      stored by the rWTM firmware.
> +	 *   3. Address of the buffer where ECDSA signature value S shall be
> +	 *      stored by the rWTM firmware.
> +	 */
> +	memset(rwtm->buf, 0, 4);
> +	ret = simple_write_to_buffer(rwtm->buf + 4, 64, &dummy, buf, len);
> +	if (ret < 0)
> +		goto unlock_mutex;
> +	be32_to_cpu_array(rwtm->buf, rwtm->buf, 17);
> +
> +	msg.command = MBOX_CMD_SIGN;
> +	msg.args[0] = 1;
> +	msg.args[1] = rwtm->buf_phys;
> +	msg.args[2] = rwtm->buf_phys + 68;
> +	msg.args[3] = rwtm->buf_phys + 2 * 68;
> +	ret = mbox_send_message(rwtm->mbox, &msg);
> +	if (ret < 0)
> +		goto unlock_mutex;
> +
> +	ret = wait_for_completion_interruptible(&rwtm->cmd_done);
> +	if (ret < 0)
> +		goto unlock_mutex;
> +
> +	ret = MBOX_STS_VALUE(reply->retval);
> +	if (MBOX_STS_ERROR(reply->retval) != MBOX_STS_SUCCESS)
> +		goto unlock_mutex;
> +
> +	/*
> +	 * Here we read the R and S values of the ECDSA signature
> +	 * computed by the rWTM firmware and convert their words from
> +	 * LE to BE.
> +	 */
> +	memcpy(rwtm->last_sig, rwtm->buf + 68, 136);
> +	cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, 34);
> +	rwtm->last_sig_done = 1;
> +
> +	mutex_unlock(&rwtm->busy);
> +	return len;
> +unlock_mutex:
> +	mutex_unlock(&rwtm->busy);
> +	return ret;
> +}
> +
> +static const struct file_operations do_sign_fops = {
> +	.owner	= THIS_MODULE,
> +	.open	= rwtm_debug_open,
> +	.read	= do_sign_read,
> +	.write	= do_sign_write,
> +	.llseek	= no_llseek,
> +};
> +
> +static int rwtm_register_debugfs(struct mox_rwtm *rwtm)
> +{
> +	struct dentry *root, *entry;
> +
> +	root = debugfs_create_dir("turris-mox-rwtm", NULL);
> +
> +	if (IS_ERR(root))
> +		return PTR_ERR(root);
> +
> +	entry = debugfs_create_file_unsafe("do_sign", 0600, root, rwtm,
> +					   &do_sign_fops);
> +	if (IS_ERR(entry))
> +		goto err_remove;
> +
> +	rwtm->debugfs_root = root;
> +
> +	return 0;
> +err_remove:
> +	debugfs_remove_recursive(root);
> +	return PTR_ERR(entry);
> +}
> +
> +static void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
> +{
> +	debugfs_remove_recursive(rwtm->debugfs_root);
> +}
> +#else
> +static inline int rwtm_register_debugfs(struct mox_rwtm *rwtm)
> +{
> +	return 0;
> +}
> +
> +static inline void rwtm_unregister_debugfs(struct mox_rwtm *rwtm)
> +{
> +}
> +#endif
> +
>  static int turris_mox_rwtm_probe(struct platform_device *pdev)
>  {
>  	struct mox_rwtm *rwtm;
> @@ -340,6 +499,12 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev)
>  		goto free_channel;
>  	}
>  
> +	ret = rwtm_register_debugfs(rwtm);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed creating debugfs entries: %i\n", ret);
> +		goto free_channel;
> +	}
> +
>  	return 0;
>  
>  free_channel:
> @@ -355,6 +520,7 @@ static int turris_mox_rwtm_remove(struct platform_device *pdev)
>  {
>  	struct mox_rwtm *rwtm = platform_get_drvdata(pdev);
>  
> +	rwtm_unregister_debugfs(rwtm);
>  	sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
>  	kobject_put(rwtm_to_kobj(rwtm));
>  	mbox_free_channel(rwtm->mbox);
> -- 
> 2.26.2
>

-- 
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com



More information about the linux-arm-kernel mailing list