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