Skip to content

Commit df94a2f

Browse files
elkabloarndb
authored andcommitted
platform: cznic: turris-omnia-mcu: Add support for digital message signing with HW private key
Add support for digital message signing with the private key stored in the MCU. Turris Omnia boards with MKL MCUs have a NIST256p ECDSA private key generated and burned into MCU's flash when manufactured. The private key is not readable from the MCU, but MCU allows for signing messages with it and retrieving the public key. This is exposed to userspace via the keyctl API. In userspace, the user can look at /proc/keys or list the keyring: $ cat /proc/keys 0a3b7cd3 ... keyring .turris-signing-keys: 1 3caf0b1a ... turris-om Turris Omnia SN 0000000A1000023 MCU ECDSA k... $ keyctl rlist %:.turris-signing-keys 1018104602 To get the public key: $ keyctl read 1018104602 33 bytes of data in key: 025d9108 1fb538ae 8435c88b b4379171 d6b158a9 55751b91 1d23e6a9 d017f4b2 1c To sign a message: $ dd if=/dev/urandom of=msg_to_sign bs=32 count=1 $ keyctl pkey_sign 1018104602 0 msg_to_sign >signature Signed-off-by: Marek Behún <[email protected]> Signed-off-by: Arnd Bergmann <[email protected]>
1 parent 0b28b70 commit df94a2f

File tree

5 files changed

+208
-0
lines changed

5 files changed

+208
-0
lines changed

drivers/platform/cznic/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ config TURRIS_OMNIA_MCU_TRNG
7575
Say Y here to add support for the true random number generator
7676
provided by CZ.NIC's Turris Omnia MCU.
7777

78+
config TURRIS_OMNIA_MCU_KEYCTL
79+
bool "Turris Omnia MCU ECDSA message signing"
80+
default y
81+
depends on KEYS
82+
depends on ASYMMETRIC_KEY_TYPE
83+
depends on TURRIS_OMNIA_MCU_GPIO
84+
select TURRIS_SIGNING_KEY
85+
help
86+
Say Y here to add support for ECDSA message signing with board private
87+
key (if available on the MCU). This is exposed via the keyctl()
88+
syscall.
89+
7890
endif # TURRIS_OMNIA_MCU
7991

8092
config TURRIS_SIGNING_KEY

drivers/platform/cznic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
44
turris-omnia-mcu-y := turris-omnia-mcu-base.o
55
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o
6+
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_KEYCTL) += turris-omnia-mcu-keyctl.o
67
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o
78
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o
89
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o

drivers/platform/cznic/turris-omnia-mcu-base.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ static int omnia_mcu_probe(struct i2c_client *client)
392392
if (err)
393393
return err;
394394

395+
err = omnia_mcu_register_keyctl(mcu);
396+
if (err)
397+
return err;
398+
395399
return omnia_mcu_register_trng(mcu);
396400
}
397401

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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 <[email protected]>
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+
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+
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+
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+
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+
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+
}

drivers/platform/cznic/turris-omnia-mcu.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
#include <linux/watchdog.h>
1919
#include <linux/workqueue.h>
2020

21+
enum {
22+
OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN = 1 + 32,
23+
OMNIA_MCU_CRYPTO_SIGNATURE_LEN = 64,
24+
};
25+
2126
struct i2c_client;
2227
struct rtc_device;
2328

@@ -56,6 +61,12 @@ struct rtc_device;
5661
* @wdt: watchdog driver structure
5762
* @trng: RNG driver structure
5863
* @trng_entropy_ready: RNG entropy ready completion
64+
* @msg_signed: message signed completion
65+
* @sign_lock: mutex to protect message signing state
66+
* @sign_requested: flag indicating that message signing was requested but not completed
67+
* @sign_err: message signing error number, filled in interrupt handler
68+
* @signature: message signing signature, filled in interrupt handler
69+
* @board_public_key: board public key, if stored in MCU
5970
*/
6071
struct omnia_mcu {
6172
struct i2c_client *client;
@@ -89,6 +100,15 @@ struct omnia_mcu {
89100
struct hwrng trng;
90101
struct completion trng_entropy_ready;
91102
#endif
103+
104+
#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL
105+
struct completion msg_signed;
106+
struct mutex sign_lock;
107+
bool sign_requested;
108+
int sign_err;
109+
u8 signature[OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
110+
u8 board_public_key[OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
111+
#endif
92112
};
93113

94114
#ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
@@ -103,6 +123,15 @@ static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
103123
}
104124
#endif
105125

126+
#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL
127+
int omnia_mcu_register_keyctl(struct omnia_mcu *mcu);
128+
#else
129+
static inline int omnia_mcu_register_keyctl(struct omnia_mcu *mcu)
130+
{
131+
return 0;
132+
}
133+
#endif
134+
106135
#ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
107136
extern const struct attribute_group omnia_mcu_poweroff_group;
108137
int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);

0 commit comments

Comments
 (0)