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