File Index
Files and directories
Sign in
FILE

turris-omnia-mcu-keyctl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/cznic/turris-omnia-mcu-keyctl.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * CZ.NIC's Turris Omnia MCU ECDSA message signing via keyctl
  4 *
  5 * 2025 by Marek BehĂșn <kabel@kernel.org>
  6 */
  7
  8#include <crypto/sha2.h>
  9#include <linux/cleanup.h>
 10#include <linux/completion.h>
 11#include <linux/device.h>
 12#include <linux/err.h>
 13#include <linux/i2c.h>
 14#include <linux/interrupt.h>
 15#include <linux/key.h>
 16#include <linux/mutex.h>
 17#include <linux/string.h>
 18#include <linux/types.h>
 19
 20#include <linux/turris-omnia-mcu-interface.h>
 21#include <linux/turris-signing-key.h>
 22#include "turris-omnia-mcu.h"
 23
 24static irqreturn_t omnia_msg_signed_irq_handler(int irq, void *dev_id)
 25{
 26	u8 reply[1 + OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
 27	struct omnia_mcu *mcu = dev_id;
 28	int err;
 29
 30	err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE,
 31			     reply, sizeof(reply));
 32	if (!err && reply[0] != OMNIA_MCU_CRYPTO_SIGNATURE_LEN)
 33		err = -EIO;
 34
 35	guard(mutex)(&mcu->sign_lock);
 36
 37	if (mcu->sign_requested) {
 38		mcu->sign_err = err;
 39		if (!err)
 40			memcpy(mcu->signature, &reply[1],
 41			       OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
 42		mcu->sign_requested = false;
 43		complete(&mcu->msg_signed);
 44	}
 45
 46	return IRQ_HANDLED;
 47}
 48
 49static int omnia_mcu_sign(const struct key *key, const void *msg,
 50			  void *signature)
 51{
 52	struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
 53	u8 cmd[1 + SHA256_DIGEST_SIZE], reply;
 54	int err;
 55
 56	scoped_guard(mutex, &mcu->sign_lock) {
 57		if (mcu->sign_requested)
 58			return -EBUSY;
 59
 60		cmd[0] = OMNIA_CMD_CRYPTO_SIGN_MESSAGE;
 61		memcpy(&cmd[1], msg, SHA256_DIGEST_SIZE);
 62
 63		err = omnia_cmd_write_read(mcu->client, cmd, sizeof(cmd),
 64					   &reply, 1);
 65		if (err)
 66			return err;
 67
 68		if (!reply)
 69			return -EBUSY;
 70
 71		mcu->sign_requested = true;
 72	}
 73
 74	if (wait_for_completion_interruptible(&mcu->msg_signed))
 75		return -EINTR;
 76
 77	guard(mutex)(&mcu->sign_lock);
 78
 79	if (mcu->sign_err)
 80		return mcu->sign_err;
 81
 82	memcpy(signature, mcu->signature, OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
 83
 84	/* forget the signature, for security */
 85	memzero_explicit(mcu->signature, sizeof(mcu->signature));
 86
 87	return OMNIA_MCU_CRYPTO_SIGNATURE_LEN;
 88}
 89
 90static const void *omnia_mcu_get_public_key(const struct key *key)
 91{
 92	struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
 93
 94	return mcu->board_public_key;
 95}
 96
 97static const struct turris_signing_key_subtype omnia_signing_key_subtype = {
 98	.key_size		= 256,
 99	.data_size		= SHA256_DIGEST_SIZE,
100	.sig_size		= OMNIA_MCU_CRYPTO_SIGNATURE_LEN,
101	.public_key_size	= OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN,
102	.hash_algo		= "sha256",
103	.get_public_key		= omnia_mcu_get_public_key,
104	.sign			= omnia_mcu_sign,
105};
106
107static int omnia_mcu_read_public_key(struct omnia_mcu *mcu)
108{
109	u8 reply[1 + OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
110	int err;
111
112	err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY,
113			     reply, sizeof(reply));
114	if (err)
115		return err;
116
117	if (reply[0] != OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN)
118		return -EIO;
119
120	memcpy(mcu->board_public_key, &reply[1],
121	       OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN);
122
123	return 0;
124}
125
126int omnia_mcu_register_keyctl(struct omnia_mcu *mcu)
127{
128	struct device *dev = &mcu->client->dev;
129	char desc[48];
130	int err;
131
132	if (!(mcu->features & OMNIA_FEAT_CRYPTO))
133		return 0;
134
135	err = omnia_mcu_read_public_key(mcu);
136	if (err)
137		return dev_err_probe(dev, err,
138				     "Cannot read board public key\n");
139
140	err = devm_mutex_init(dev, &mcu->sign_lock);
141	if (err)
142		return err;
143
144	init_completion(&mcu->msg_signed);
145
146	err = omnia_mcu_request_irq(mcu, OMNIA_INT_MESSAGE_SIGNED,
147				    omnia_msg_signed_irq_handler,
148				    "turris-omnia-mcu-keyctl");
149	if (err)
150		return dev_err_probe(dev, err,
151				     "Cannot request MESSAGE_SIGNED IRQ\n");
152
153	sprintf(desc, "Turris Omnia SN %016llX MCU ECDSA key",
154		mcu->board_serial_number);
155
156	err = devm_turris_signing_key_create(dev, &omnia_signing_key_subtype,
157					     desc);
158	if (err)
159		return dev_err_probe(dev, err, "Cannot create signing key\n");
160
161	return 0;
162}
~/ Read-only file preview.