[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