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