Skip to contentSkip to conversation

platform: cznic: turris-omnia-mcu: Add support for digital message signing via keyctl

RFC PATCH New Marek Behún 1 participant 1 message Jul 12, 2024

Subsystems (2)
lore
Share & tools
lore

Marek Behún

author maintainer
#0 lore raw

RFC PATCH 1/1 platform: cznic: turris-omnia-mcu: Add support for digital message signing via keyctl

Add support for digital message signing with private key stored in the MCU. Boards with MKL MCUs have a NIST256p ECDSA private key created 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. The module registers key type "turris-omnia-mcu", and on device probe, if the board supports this feature, it allocates the keyring ".turris-omnia-mcu-keys" with one key.

In userspace, the user can look at /proc/keys or list the keyring:

  $ cat /proc/keys
  0a3b7cd3 ... keyring   .turris-omnia-mcu-keys: 1
  3caf0b1a ... turris-om Turris Omnia SN 0000000A1000023 MCU ECDSA k...
  $ keyctl rlist %:.turris-omnia-mcu-keys
  1018104602

To get 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

The key and keyring are dropped when the driver is unbound from the device.

The keyring and key are created with only the VIEW, READ and SEARCH permissions for userspace - it is impossible to link / unlink / move the key, set its attributes, or unlink the keyring from userspace.

I found one thing not possible to prevent without changing code in security/keys: the userspace can create another key of type "turris-omnia-mcu" in another keyring, for example:

  $ echo | keyctl padd turris-omnia-mcu TEST @u
  972416162

Nonetheless the driver is written so that using this invalid key that shouldn't have been create at all will return -EIO:

  $ keyctl pkey_query 972416162 0
  keyctl_pkey_query: I/O error
  $ keyctl pkey_sign 972416162 0 msg_to_sign >signature
  keyctl_pkey_query: I/O error

---
Hi Herbert et al.,

this patch depends on patches in soc/soc.git.

I would like to get your thoughts on the following:
- should this driver create the special ".turris-omnia-mcu-keys" keyring, as it does in this RFC, or should it put the key into an existing keyring, for example user specific (root) keyring? Should it allow changing the keyring?
- should this driver prohibit changing owner of the key and other attributes, as it does in this RFC, or should it allow giving access to the key to other users?

Marek

---
 drivers/platform/cznic/Kconfig                |   4 +
 drivers/platform/cznic/Makefile               |   1 +
 .../platform/cznic/turris-omnia-mcu-base.c    |  30 +-
 .../platform/cznic/turris-omnia-mcu-keyctl.c  | 294 ++++++++++++++++++
 drivers/platform/cznic/turris-omnia-mcu.h     |  15 +
 5 files changed, 342 insertions(+), 2 deletions(-)
 create mode 100644 drivers/platform/cznic/turris-omnia-mcu-keyctl.c
drivers/platform/cznic/Kconfig +4 -0
index 2a5235cf6844..9bc0f976d13f 100644
@@ -21,6 +21,8 @@ config TURRIS_OMNIA_MCU
 	select GPIOLIB
 	select GPIOLIB_IRQCHIP
 	select HW_RANDOM
+	select KEYS
+	select ASYMMETRIC_KEY_TYPE
 	select RTC_CLASS
 	select WATCHDOG_CORE
 	help
@@ -31,6 +33,8 @@ config TURRIS_OMNIA_MCU
 	    disabled) and the ability to configure wake up from this mode (via
 	    rtcwake)
 	  - true random number generator (if available on the MCU)
+	  - ECDSA message signing with board private key (if available on the
+	    MCU)
 	  - MCU watchdog
 	  - GPIO pins
 	    - to get front button press events (the front button can be
drivers/platform/cznic/Makefile +1 -0
index eae4c6b341ff..ad42cbbb4fb0 100644
@@ -3,6 +3,7 @@
 obj-$(CONFIG_TURRIS_OMNIA_MCU)	+= turris-omnia-mcu.o
 turris-omnia-mcu-y		:= turris-omnia-mcu-base.o
 turris-omnia-mcu-y		+= turris-omnia-mcu-gpio.o
+turris-omnia-mcu-y		+= turris-omnia-mcu-keyctl.o
 turris-omnia-mcu-y		+= turris-omnia-mcu-sys-off-wakeup.o
 turris-omnia-mcu-y		+= turris-omnia-mcu-trng.o
 turris-omnia-mcu-y		+= turris-omnia-mcu-watchdog.o
drivers/platform/cznic/turris-omnia-mcu-base.c +28 -2
index c68a7a84a951..b2fb4fbb33e8 100644
@@ -385,7 +385,11 @@ static int omnia_mcu_probe(struct i2c_client *client)
 	if (err)
 		return err;
 
-	return omnia_mcu_register_trng(mcu);
+	err = omnia_mcu_register_trng(mcu);
+	if (err)
+		return err;
+
+	return omnia_mcu_register_keyctl(mcu);
 }
 
 static const struct of_device_id of_omnia_mcu_match[] = {
@@ -401,7 +405,29 @@ static struct i2c_driver omnia_mcu_driver = {
 		.dev_groups = omnia_mcu_groups,
 	},
 };
-module_i2c_driver(omnia_mcu_driver);
+
+static int __init omnia_mcu_driver_init(void)
+{
+	int err;
+
+	err = omnia_mcu_keyctl_init();
+	if (err)
+		return err;
+
+	err = i2c_add_driver(&omnia_mcu_driver);
+	if (err)
+		omnia_mcu_keyctl_exit();
+
+	return err;
+}
+module_init(omnia_mcu_driver_init);
+
+static void __exit omnia_mcu_driver_exit(void)
+{
+	i2c_del_driver(&omnia_mcu_driver);
+	omnia_mcu_keyctl_exit();
+}
+module_exit(omnia_mcu_driver_exit);
 
 MODULE_AUTHOR("Marek Behun <[email protected]>");
 MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU");
drivers/platform/cznic/turris-omnia-mcu-keyctl.c new file +294 -0

294 lines not on this page · raw message

drivers/platform/cznic/turris-omnia-mcu.h +15 -0
index 2ca56ae13aa9..3371e4f635cd 100644
@@ -20,6 +20,9 @@
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
+#define OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN	(1 + 32)
+#define OMNIA_MCU_CRYPTO_SIGNATURE_LEN	64
+
 struct i2c_client;
 struct rtc_device;
 
@@ -32,6 +35,7 @@ struct omnia_mcu {
 	u64 board_serial_number;
 	u8 board_first_mac[ETH_ALEN];
 	u8 board_revision;
+	u8 board_public_key[OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
 
 	/* GPIO chip */
 	struct gpio_chip gc;
@@ -53,6 +57,13 @@ struct omnia_mcu {
 	/* true random number generator */
 	struct hwrng trng;
 	struct completion trng_entropy_ready;
+
+	/* MCU ECDSA message signing via keyctl */
+	struct completion msg_signed_completion;
+	struct mutex sign_lock;
+	bool sign_requested;
+	u8 signature[OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
+	int sign_err;
 };
 
 int omnia_cmd_write_read(const struct i2c_client *client,
@@ -187,8 +198,12 @@ extern const struct attribute_group omnia_mcu_gpio_group;
 extern const struct attribute_group omnia_mcu_poweroff_group;
 
 int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
+int omnia_mcu_register_keyctl(struct omnia_mcu *mcu);
 int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
 int omnia_mcu_register_trng(struct omnia_mcu *mcu);
 int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
 
+int omnia_mcu_keyctl_init(void);
+void omnia_mcu_keyctl_exit(void);
+
 #endif /* __TURRIS_OMNIA_MCU_H */
Signature · 1 line
2.44.2