xref: /linux/drivers/platform/cznic/turris-omnia-mcu-keyctl.c (revision 297d9111e9fcf47dd1dcc6f79bba915f35378d01)
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 
omnia_msg_signed_irq_handler(int irq,void * dev_id)24 static 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 
omnia_mcu_sign(const struct key * key,const void * msg,void * signature)49 static 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 
omnia_mcu_get_public_key(const struct key * key)90 static 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 
97 static 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 
omnia_mcu_read_public_key(struct omnia_mcu * mcu)107 static 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 
omnia_mcu_register_keyctl(struct omnia_mcu * mcu)126 int 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 }
163