From patchwork Tue Feb 4 13:14:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 13959177 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7706E20DD71 for ; Tue, 4 Feb 2025 13:14:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674869; cv=none; b=QOYhS3Fq/QLIdHUTwm3yojFA3iofKgEmELRQH9Zdlh3DXthtWP9NrRqRf1eZ1BwYZLlDqopQFianQYBYrY4vjubBIG+hwbCRF6PfdynK8TIWxPNe6oozqV8r3EDEHeET4rObLQ6/f75sCMWGyQPn8o1D9CxylKSj9R6VIduce18= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674869; c=relaxed/simple; bh=C4Y0oFTe87ATUsaE5Py2t5kX/LwIzq+3MQv5G5HA0WA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UNiAMW2L4VHE66SnFI1lROaxylLdY/SAXyHIEvKnJWWAfShtCbtJbDd1PVQ06mCyFHQW5j5FLfy3mqTMidKygcs+sFOP/HRsebUmFy/VXqEUtK4gZALCqw30vAQ5C/VfAgF3lSiElJeRWqfYHQBAY9oCpebzBKeYxT9C0ZISfPA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YBfKmrUF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YBfKmrUF" Received: by smtp.kernel.org (Postfix) id 68045C4CEE4; Tue, 4 Feb 2025 13:14:29 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8DF47C4AF09; Tue, 4 Feb 2025 13:14:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738674869; bh=C4Y0oFTe87ATUsaE5Py2t5kX/LwIzq+3MQv5G5HA0WA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YBfKmrUFm5hJvgxoIUuqE2AUIK6nQELHLG/hTTs3zDrDFpAvbiM0nnDh7ypBO2u2n 7tpCdL0oIaSxr+K03i2fGyEaDir4kKq3gKicnsZVP2acfOVfXZ2KfYQWi+JkFl/QNp etOlhN8byVK0Fa9SqzPkCJS95NnjpXLSDJ0TuKCsqiIbkpWnlb8ZbT1hgraPPS3x/B WEZ0/hZhFzN6VSVR2dFnPgtqoAj2t1nUpmymfNsZ9OxwbjaI1RwWfewhQlCrywLwVc /7u1S61mnX2uFLvmRQOMLZiaDPhr8NkgSskryjkeQiNpyqKDycOlixd8g0wj3FOpGz kYr2Y3ZLqQS9Q== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Arnd Bergmann , soc@kernel.org Cc: arm@kernel.org, Andy Shevchenko , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , linux-crypto@vger.kernel.org, Herbert Xu , Greg Kroah-Hartman , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH 1/5] platform: cznic: turris-omnia-mcu: Refactor requesting MCU interrupt Date: Tue, 4 Feb 2025 14:14:11 +0100 Message-ID: <20250204131415.27014-2-kabel@kernel.org> X-Mailer: git-send-email 2.45.3 In-Reply-To: <20250204131415.27014-1-kabel@kernel.org> References: <20250204131415.27014-1-kabel@kernel.org> Precedence: bulk X-Mailing-List: soc@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Refactor the code that gets and requests the TRNG MCU interrupt in the TRNG part of the driver into a helper function and put it into the GPIO part of the driver. Signed-off-by: Marek Behún --- .../platform/cznic/turris-omnia-mcu-gpio.c | 21 ++++++++++++++++++- .../platform/cznic/turris-omnia-mcu-trng.c | 17 ++++----------- drivers/platform/cznic/turris-omnia-mcu.h | 4 +++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/drivers/platform/cznic/turris-omnia-mcu-gpio.c b/drivers/platform/cznic/turris-omnia-mcu-gpio.c index 5f35f7c5d5d7..932383f7491a 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c +++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -195,7 +196,7 @@ static const struct omnia_gpio omnia_gpios[64] = { }; /* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ -const u8 omnia_int_to_gpio_idx[32] = { +static const u8 omnia_int_to_gpio_idx[32] = { [__bf_shf(OMNIA_INT_CARD_DET)] = 4, [__bf_shf(OMNIA_INT_MSATA_IND)] = 5, [__bf_shf(OMNIA_INT_USB30_OVC)] = 6, @@ -1093,3 +1094,21 @@ int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu) return 0; } + +int omnia_mcu_request_irq(struct omnia_mcu *mcu, u32 spec, + irq_handler_t thread_fn, const char *devname) +{ + u8 irq_idx; + int irq; + + if (!spec) + return -EINVAL; + + irq_idx = omnia_int_to_gpio_idx[__bf_shf(spec)]; + irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx)); + if (irq < 0) + return irq; + + return devm_request_threaded_irq(&mcu->client->dev, irq, NULL, + thread_fn, IRQF_ONESHOT, devname, mcu); +} diff --git a/drivers/platform/cznic/turris-omnia-mcu-trng.c b/drivers/platform/cznic/turris-omnia-mcu-trng.c index 9a1d9292dc9a..e3826959e6de 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-trng.c +++ b/drivers/platform/cznic/turris-omnia-mcu-trng.c @@ -5,12 +5,9 @@ * 2024 by Marek Behún */ -#include #include #include #include -#include -#include #include #include #include @@ -62,17 +59,12 @@ static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) int omnia_mcu_register_trng(struct omnia_mcu *mcu) { struct device *dev = &mcu->client->dev; - u8 irq_idx, dummy; - int irq, err; + u8 dummy; + int err; if (!(mcu->features & OMNIA_FEAT_TRNG)) return 0; - irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)]; - irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx)); - if (irq < 0) - return dev_err_probe(dev, irq, "Cannot get TRNG IRQ\n"); - /* * If someone else cleared the TRNG interrupt but did not read the * entropy, a new interrupt won't be generated, and entropy collection @@ -86,9 +78,8 @@ int omnia_mcu_register_trng(struct omnia_mcu *mcu) init_completion(&mcu->trng_entropy_ready); - err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler, - IRQF_ONESHOT, "turris-omnia-mcu-trng", - mcu); + err = omnia_mcu_request_irq(mcu, OMNIA_INT_TRNG, omnia_trng_irq_handler, + "turris-omnia-mcu-trng"); if (err) return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n"); diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h index 088541be3f4c..f2084880a00c 100644 --- a/drivers/platform/cznic/turris-omnia-mcu.h +++ b/drivers/platform/cznic/turris-omnia-mcu.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -91,9 +92,10 @@ struct omnia_mcu { }; #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO -extern const u8 omnia_int_to_gpio_idx[32]; extern const struct attribute_group omnia_mcu_gpio_group; int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); +int omnia_mcu_request_irq(struct omnia_mcu *mcu, u32 spec, + irq_handler_t thread_fn, const char *devname); #else static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu) { From patchwork Tue Feb 4 13:14:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 13959178 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E866E20DD71 for ; Tue, 4 Feb 2025 13:14:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674875; cv=none; b=HsnRUAQhpzi4GnfNyHvHYyBwg46shPSo8C5E4BoCf5fTanpqQv292zhehDTwMkUCyt5v8sXTWGiXp43GAEHIJbzXk1L8MwvLU59ksL41VzuBFcYk/KzRXikyxFc1TgbrQyjG9cXnui/Q3wCBkrejTj7Hc7aRJDONw8O45M+z4zI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674875; c=relaxed/simple; bh=x1ygPA8J4qnuyDNUMKtqKy91nBFfJUfK5w83C12NV2Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GHG1EXBBuuWt2eO1/kDp05OZvzujLrTlU7Xv10lm1Jl6vmUp/cX0pZgn9SnOxvNm2pKZi7Iy2aupR7fAemsezFeON49KHYXFozmHQ1LWgPnWGtS/hTOvYt+hOuOwg6Vv1MWjFy6q6rNR2WC5T9T3+atgR5m8XVb59e61mBjqSyE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xy1VEjDK; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xy1VEjDK" Received: by smtp.kernel.org (Postfix) id DD5D5C4CEE2; Tue, 4 Feb 2025 13:14:34 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D8F4DC4CEEA; Tue, 4 Feb 2025 13:14:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738674874; bh=x1ygPA8J4qnuyDNUMKtqKy91nBFfJUfK5w83C12NV2Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Xy1VEjDK7Mv/Bad0x5ax8F/X87+Jb0ktSc8Q11SbC4EeRRhi6k8u3nG2yhndX8RuI hgrKqWFJrd1AyyrqO1qK0RcLnCHrNvC2SwueWjs+wrBAqd9mXOKjKrGbO123jHgnUq nXFwJhv604JjCpFuLUgS2JpLdnPym0jQ15cIdEMr8f0aA5HJxnvF0F3ahwTxRM5ztU ombP8ozvOqGU31j/kZXAgjPHxCXroRURL7vRWmLDmhZpCDO+KpJ1VTJDJTNUbQiTI5 YG4ULW0ZHlZ+EkQFysKTDU/570hOS7IhCBpVJ38vR3r1bO65ZD9iq6v+AvcXpvEVdW T6rhTUOXebvog== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Arnd Bergmann , soc@kernel.org Cc: arm@kernel.org, Andy Shevchenko , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , linux-crypto@vger.kernel.org, Herbert Xu , Greg Kroah-Hartman , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH 2/5] platform: cznic: Add keyctl helpers for Turris platform Date: Tue, 4 Feb 2025 14:14:12 +0100 Message-ID: <20250204131415.27014-3-kabel@kernel.org> X-Mailer: git-send-email 2.45.3 In-Reply-To: <20250204131415.27014-1-kabel@kernel.org> References: <20250204131415.27014-1-kabel@kernel.org> Precedence: bulk X-Mailing-List: soc@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Some Turris devices support signing messages with a per-device unique asymmetric key that was created on the device at manufacture time. Add helper module that helps to expose this ability via the keyctl() syscall. A device-specific driver can register a signing key by calling devm_turris_signing_key_create(). Both the `.turris-signing-keys` keyring and the signing key are created with only the VIEW, READ and SEARCH permissions for userspace - it is impossible to link / unlink / move them, set their attributes, or unlink the keyring from userspace. Signed-off-by: Marek Behún --- MAINTAINERS | 1 + drivers/platform/cznic/Kconfig | 5 + drivers/platform/cznic/Makefile | 2 + drivers/platform/cznic/turris-signing-key.c | 192 ++++++++++++++++++++ include/linux/turris-signing-key.h | 33 ++++ 5 files changed, 233 insertions(+) create mode 100644 drivers/platform/cznic/turris-signing-key.c create mode 100644 include/linux/turris-signing-key.h diff --git a/MAINTAINERS b/MAINTAINERS index 896a307fa065..e37c3f3425a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2442,6 +2442,7 @@ F: include/dt-bindings/bus/moxtet.h F: include/linux/armada-37xx-rwtm-mailbox.h F: include/linux/moxtet.h F: include/linux/turris-omnia-mcu-interface.h +F: include/linux/turris-signing-key.h ARM/FARADAY FA526 PORT M: Hans Ulli Kroll diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig index 49c383eb6785..2b4c91ede6d8 100644 --- a/drivers/platform/cznic/Kconfig +++ b/drivers/platform/cznic/Kconfig @@ -77,4 +77,9 @@ config TURRIS_OMNIA_MCU_TRNG endif # TURRIS_OMNIA_MCU +config TURRIS_SIGNING_KEY + tristate + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + endif # CZNIC_PLATFORMS diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile index ce6d997f34d6..2b8606a9486b 100644 --- a/drivers/platform/cznic/Makefile +++ b/drivers/platform/cznic/Makefile @@ -6,3 +6,5 @@ turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o + +obj-$(CONFIG_TURRIS_SIGNING_KEY) += turris-signing-key.o diff --git a/drivers/platform/cznic/turris-signing-key.c b/drivers/platform/cznic/turris-signing-key.c new file mode 100644 index 000000000000..3b12e5245fb7 --- /dev/null +++ b/drivers/platform/cznic/turris-signing-key.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Some of CZ.NIC's Turris devices support signing messages with a per-device unique asymmetric + * cryptographic key that was burned into the device at manufacture. + * + * This helper module exposes this message signing ability via the keyctl() syscall. Upon load, it + * creates the `.turris-signing-keys` keyring. A device-specific driver then has to create a signing + * key by calling devm_turris_signing_key_create(). + * + * 2025 by Marek Behún + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int turris_signing_key_instantiate(struct key *, struct key_preparsed_payload *) +{ + return 0; +} + +static void turris_signing_key_describe(const struct key *key, struct seq_file *m) +{ + const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key); + + if (!subtype) + return; + + seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size, + subtype->get_public_key(key)); +} + +static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen) +{ + const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key); + + if (!subtype) + return -EIO; + + if (buffer) { + if (buflen > subtype->public_key_size) + buflen = subtype->public_key_size; + + memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size); + } + + return subtype->public_key_size; +} + +static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype, + const struct kernel_pkey_params *params) +{ + if (params->encoding && strcmp(params->encoding, "raw")) + return false; + + if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo)) + return false; + + return true; +} + +static int turris_signing_key_asym_query(const struct kernel_pkey_params *params, + struct kernel_pkey_query *info) +{ + const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key); + + if (!subtype) + return -EIO; + + if (!turris_signing_key_asym_valid_params(subtype, params)) + return -EINVAL; + + info->supported_ops = KEYCTL_SUPPORTS_SIGN; + info->key_size = subtype->key_size; + info->max_data_size = subtype->data_size; + info->max_sig_size = subtype->sig_size; + info->max_enc_size = 0; + info->max_dec_size = 0; + + return 0; +} + +static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params, + const void *in, void *out) +{ + const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key); + int err; + + if (!subtype) + return -EIO; + + if (!turris_signing_key_asym_valid_params(subtype, params)) + return -EINVAL; + + if (params->op != kernel_pkey_sign) + return -EOPNOTSUPP; + + if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size) + return -EINVAL; + + err = subtype->sign(params->key, in, out); + if (err) + return err; + + return subtype->sig_size; +} + +static struct key_type turris_signing_key_type = { + .name = "turris-signing-key", + .instantiate = turris_signing_key_instantiate, + .describe = turris_signing_key_describe, + .read = turris_signing_key_read, + .asym_query = turris_signing_key_asym_query, + .asym_eds_op = turris_signing_key_asym_eds_op, +}; + +static struct key *turris_signing_keyring; + +static void turris_signing_key_release(void *key) +{ + key_unlink(turris_signing_keyring, key); + key_put(key); +} + +int +devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype, + const char *desc) +{ + struct key *key; + key_ref_t kref; + + kref = key_create(make_key_ref(turris_signing_keyring, true), + turris_signing_key_type.name, desc, NULL, 0, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_SEARCH, + KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(kref)) + return PTR_ERR(kref); + + key = key_ref_to_ptr(kref); + key->payload.data[1] = dev; + rcu_assign_keypointer(key, subtype); + + return devm_add_action_or_reset(dev, turris_signing_key_release, key); +} +EXPORT_SYMBOL_GPL(devm_turris_signing_key_create); + +static int turris_signing_key_init(void) +{ + int err; + + err = register_key_type(&turris_signing_key_type); + if (err) + return err; + + turris_signing_keyring = keyring_alloc(".turris-signing-keys", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | + KEY_USR_READ | KEY_USR_SEARCH, + KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(turris_signing_keyring)) { + pr_err("Cannot allocate Turris keyring\n"); + + unregister_key_type(&turris_signing_key_type); + + return PTR_ERR(turris_signing_keyring); + } + + return 0; +} +module_init(turris_signing_key_init); + +static void turris_signing_key_exit(void) +{ + key_put(turris_signing_keyring); + unregister_key_type(&turris_signing_key_type); +} +module_exit(turris_signing_key_exit); + +MODULE_AUTHOR("Marek Behun "); +MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/turris-signing-key.h b/include/linux/turris-signing-key.h new file mode 100644 index 000000000000..032ca8cbf636 --- /dev/null +++ b/include/linux/turris-signing-key.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 2025 by Marek Behún + */ + +#ifndef __TURRIS_SIGNING_KEY_H +#define __TURRIS_SIGNING_KEY_H + +#include +#include + +struct device; + +struct turris_signing_key_subtype { + u16 key_size; + u8 data_size; + u8 sig_size; + u8 public_key_size; + const char *hash_algo; + const void *(*get_public_key)(const struct key *key); + int (*sign)(const struct key *key, const void *msg, void *signature); +}; + +static inline struct device *turris_signing_key_get_dev(const struct key *key) +{ + return key->payload.data[1]; +} + +int +devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype, + const char *desc); + +#endif /* __TURRIS_SIGNING_KEY_H */ From patchwork Tue Feb 4 13:14:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 13959179 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ABC9121770D for ; Tue, 4 Feb 2025 13:14:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674884; cv=none; b=daDcGwCFUefW5bqP/PvIrH/vbg0I43blw43jHIC5J2QI6OG5DdPB2/BiLkCEd7RVkzZpvybjbHwU38z2NXOkJhWUc7zMAJN/2Tb1BNIWlfhOKqLAYTsHOeaRXRTgNgXM/9D0Eo026kCJ0CvY4U8GLbRufJrj7aiJxJERIV1afNI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674884; c=relaxed/simple; bh=mUuhldH3XFGgE5Bt6wurmm0YH4002fJXCdOHWBu31FM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FriaNzn3X1DTZ8dgAWa/nd6pUV1WpE9AjnJ0R7F0Yg+MLB4Hz7T/VVxrqga+Aq6T0pXtgqPpyeZCyz+w6ZnkrEXe4Tf4VkVj8wTPCcwx9hmh4/9IKwQbYjnWqRv6M5ZBzvnT2jBlsOxIMx4a0i2fSte+69yn5spl4c0DqYncUAw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xce2ANRd; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xce2ANRd" Received: by smtp.kernel.org (Postfix) id 4E8E1C4AF09; Tue, 4 Feb 2025 13:14:44 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 39B61C4CEDF; Tue, 4 Feb 2025 13:14:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738674884; bh=mUuhldH3XFGgE5Bt6wurmm0YH4002fJXCdOHWBu31FM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Xce2ANRdMeXx0FejqBdqRMKEe0MziS9zBDEqR4ISfpilXA0crLAp0617z0OzHh4Bp LOEOD04BjuzrfbZcI/GeYvaLpyznV0R66zJOa6zTrgumS3hJ69QmhA0S4uIi1DCOMI smkMNnSuoautaPRuWMfJjVpSQcNYQT6co48HCTPekYmGo/nH1fQ2igqbtdflWuUX26 KsgqBmPJedt2uDge4zIPp7zm3tiaVxXC+hCJtO7XurTN2cNh8o95VNLijiHe8fE78m /GtQ1ygjWnHn9u7dln7Ar0X0niOXm1FxtLN9gcSu3ueZ0bAklgajOm8MUQOdYf8W/A M6KevN7N9WgLw== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Arnd Bergmann , soc@kernel.org Cc: arm@kernel.org, Andy Shevchenko , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , linux-crypto@vger.kernel.org, Herbert Xu , Greg Kroah-Hartman , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH 3/5] platform: cznic: turris-omnia-mcu: Add support for digital message signing with HW private key Date: Tue, 4 Feb 2025 14:14:13 +0100 Message-ID: <20250204131415.27014-4-kabel@kernel.org> X-Mailer: git-send-email 2.45.3 In-Reply-To: <20250204131415.27014-1-kabel@kernel.org> References: <20250204131415.27014-1-kabel@kernel.org> Precedence: bulk X-Mailing-List: soc@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 --- drivers/platform/cznic/Kconfig | 12 ++ drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 4 + .../platform/cznic/turris-omnia-mcu-keyctl.c | 162 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 29 ++++ 5 files changed, 208 insertions(+) create mode 100644 drivers/platform/cznic/turris-omnia-mcu-keyctl.c diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig index 2b4c91ede6d8..8318beb7ec94 100644 --- a/drivers/platform/cznic/Kconfig +++ b/drivers/platform/cznic/Kconfig @@ -75,6 +75,18 @@ config TURRIS_OMNIA_MCU_TRNG Say Y here to add support for the true random number generator provided by CZ.NIC's Turris Omnia MCU. +config TURRIS_OMNIA_MCU_KEYCTL + bool "Turris Omnia MCU ECDSA message signing" + default y + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + depends on TURRIS_OMNIA_MCU_GPIO + select TURRIS_SIGNING_KEY + help + Say Y here to add support for ECDSA message signing with board private + key (if available on the MCU). This is exposed via the keyctl() + syscall. + endif # TURRIS_OMNIA_MCU config TURRIS_SIGNING_KEY diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile index 2b8606a9486b..ccad7bec82e1 100644 --- a/drivers/platform/cznic/Makefile +++ b/drivers/platform/cznic/Makefile @@ -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-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o +turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_KEYCTL) += turris-omnia-mcu-keyctl.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c index 770e680b96f9..e8fc0d7b3343 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-base.c +++ b/drivers/platform/cznic/turris-omnia-mcu-base.c @@ -392,6 +392,10 @@ static int omnia_mcu_probe(struct i2c_client *client) if (err) return err; + err = omnia_mcu_register_keyctl(mcu); + if (err) + return err; + return omnia_mcu_register_trng(mcu); } diff --git a/drivers/platform/cznic/turris-omnia-mcu-keyctl.c b/drivers/platform/cznic/turris-omnia-mcu-keyctl.c new file mode 100644 index 000000000000..dc40f942f082 --- /dev/null +++ b/drivers/platform/cznic/turris-omnia-mcu-keyctl.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CZ.NIC's Turris Omnia MCU ECDSA message signing via keyctl + * + * 2025 by Marek Behún + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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; +} diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h index f2084880a00c..8473a3031917 100644 --- a/drivers/platform/cznic/turris-omnia-mcu.h +++ b/drivers/platform/cznic/turris-omnia-mcu.h @@ -18,6 +18,11 @@ #include #include +enum { + OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN = 1 + 32, + OMNIA_MCU_CRYPTO_SIGNATURE_LEN = 64, +}; + struct i2c_client; struct rtc_device; @@ -56,6 +61,12 @@ struct rtc_device; * @wdt: watchdog driver structure * @trng: RNG driver structure * @trng_entropy_ready: RNG entropy ready completion + * @msg_signed: message signed completion + * @sign_lock: mutex to protect message signing state + * @sign_requested: flag indicating that message signing was requested but not completed + * @sign_err: message signing error number, filled in interrupt handler + * @signature: message signing signature, filled in interrupt handler + * @board_public_key: board public key, if stored in MCU */ struct omnia_mcu { struct i2c_client *client; @@ -89,6 +100,15 @@ struct omnia_mcu { struct hwrng trng; struct completion trng_entropy_ready; #endif + +#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL + struct completion msg_signed; + struct mutex sign_lock; + bool sign_requested; + int sign_err; + u8 signature[OMNIA_MCU_CRYPTO_SIGNATURE_LEN]; + u8 board_public_key[OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN]; +#endif }; #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO @@ -103,6 +123,15 @@ static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu) } #endif +#ifdef CONFIG_TURRIS_OMNIA_MCU_KEYCTL +int omnia_mcu_register_keyctl(struct omnia_mcu *mcu); +#else +static inline int omnia_mcu_register_keyctl(struct omnia_mcu *mcu) +{ + return 0; +} +#endif + #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP extern const struct attribute_group omnia_mcu_poweroff_group; int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu); From patchwork Tue Feb 4 13:14:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 13959180 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 94DCB45005 for ; Tue, 4 Feb 2025 13:14:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674890; cv=none; b=j1Px1DcRroydlnL3NgNxmZnJXhdMNGMz5CydHI1yXdghpmDMSwHQpHO64eG3PG/v7KwP4Ard+h5IyTtujyG89gipgz8aYb+M0TXF35Dr3HDqH53cdY2bZP98pW6SYidfiuV3LkZZ8KYkWfxPvoL3+3rJcn6lwlDf4k6yj+wNLbA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674890; c=relaxed/simple; bh=tUbIHhIOslf+2m8/EY8pTv4Hgm6scR82m6ZQ4Jyqaps=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=uacJI4+cPhidMQa1aMFPB2Waq/wlbHvANGVSwHiwEVDuWcIcAcLeQmYMC2s2MqoPBQI03DR6meyayXCNSZCzFxi99joNh7wVtDg+runfv1UorLiMsvakNKnaDc1E/mDhsUHgF1Xm1/TwCnXtAw3tKR6UXPb6/aUj6C2JvIx5weQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=t32bLlbx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="t32bLlbx" Received: by smtp.kernel.org (Postfix) id 37253C4AF0C; Tue, 4 Feb 2025 13:14:50 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BE992C4CEE4; Tue, 4 Feb 2025 13:14:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738674890; bh=tUbIHhIOslf+2m8/EY8pTv4Hgm6scR82m6ZQ4Jyqaps=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t32bLlbxDySt0kZ+nrLxzzwAYCMLVjZwacRKPvnGps1b8yMeokBLjWISjWFWS0G7S jD9qbhCDbFv2M23AcPgAydgvd4B+gyW6eW4a/iiNlOLl0ryhRDLoJJcjucwKMZUYXl 5wJiueV1TRu+lTdsQNxr94fLGs92rPdvYaY/d8cw6uu9Rz7Y/Uo0VcAHjENbiSshiI bQnbXVdUfJ8+D2m7Ni8D8Qz8aumyYgtayvh/94n5/AE0FFz84BG7GH41rYEYY+rAtM 9rCXFkDklBO8JhKGVMUP22BGNTcDjnIhPmAlOnB/pih0+weyc/ZpnCLR/fJktGfkkK qq8UiR9TTyPpA== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Arnd Bergmann , soc@kernel.org Cc: arm@kernel.org, Andy Shevchenko , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , linux-crypto@vger.kernel.org, Herbert Xu , Greg Kroah-Hartman , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH 4/5] firmware: turris-mox-rwtm: Drop ECDSA signatures via debugfs Date: Tue, 4 Feb 2025 14:14:14 +0100 Message-ID: <20250204131415.27014-5-kabel@kernel.org> X-Mailer: git-send-email 2.45.3 In-Reply-To: <20250204131415.27014-1-kabel@kernel.org> References: <20250204131415.27014-1-kabel@kernel.org> Precedence: bulk X-Mailing-List: soc@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Drop the debugfs implementation of the ECDSA message signing, in preparation for a new implementation via the keyctl() syscall. Signed-off-by: Marek Behún --- .../ABI/testing/debugfs-turris-mox-rwtm | 14 -- .../testing/sysfs-firmware-turris-mox-rwtm | 9 - drivers/firmware/turris-mox-rwtm.c | 184 +----------------- 3 files changed, 7 insertions(+), 200 deletions(-) delete mode 100644 Documentation/ABI/testing/debugfs-turris-mox-rwtm diff --git a/Documentation/ABI/testing/debugfs-turris-mox-rwtm b/Documentation/ABI/testing/debugfs-turris-mox-rwtm deleted file mode 100644 index 813987d5de4e..000000000000 --- a/Documentation/ABI/testing/debugfs-turris-mox-rwtm +++ /dev/null @@ -1,14 +0,0 @@ -What: /sys/kernel/debug/turris-mox-rwtm/do_sign -Date: Jun 2020 -KernelVersion: 5.8 -Contact: Marek Behún -Description: - - ======= =========================================================== - (Write) Message to sign with the ECDSA private key stored in - device's OTP. The message must be exactly 64 bytes - (since this is intended for SHA-512 hashes). - (Read) The resulting signature, 136 bytes. This contains the - R and S values of the ECDSA signature, both in - big-endian format. - ======= =========================================================== diff --git a/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm b/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm index ea5e5b489bc7..26741cb84504 100644 --- a/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm +++ b/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm @@ -12,15 +12,6 @@ Contact: Marek Behún Description: (Read) MAC addresses burned into eFuses of this Turris Mox board. Format: %pM -What: /sys/firmware/turris-mox-rwtm/pubkey -Date: August 2019 -KernelVersion: 5.4 -Contact: Marek Behún -Description: (Read) ECDSA public key (in pubkey hex compressed form) computed - as pair to the ECDSA private key burned into eFuses of this - Turris Mox Board. - Format: string - What: /sys/firmware/turris-mox-rwtm/ram_size Date: August 2019 KernelVersion: 5.4 diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index 47fe6261f5a3..16e5f19dfafd 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -5,16 +5,13 @@ * Copyright (C) 2019, 2024 Marek Behún */ -#include #include #include #include #include -#include #include #include #include -#include #include #include #include @@ -37,11 +34,6 @@ * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. */ -#define MOX_ECC_NUMBER_WORDS 17 -#define MOX_ECC_NUMBER_LEN (MOX_ECC_NUMBER_WORDS * sizeof(u32)) - -#define MOX_ECC_SIGNATURE_WORDS (2 * MOX_ECC_NUMBER_WORDS) - #define MBOX_STS_SUCCESS (0 << 30) #define MBOX_STS_FAIL (1 << 30) #define MBOX_STS_BADCMD (2 << 30) @@ -77,10 +69,6 @@ enum mbox_cmd { * @ram_size: RAM size of the device * @mac_address1: first MAC address of the device * @mac_address2: second MAC address of the device - * @has_pubkey: whether board ECDSA public key is present - * @pubkey: board ECDSA public key - * @last_sig: last ECDSA signature generated with board ECDSA private key - * @last_sig_done: whether the last ECDSA signing is complete */ struct mox_rwtm { struct mbox_client mbox_client; @@ -99,20 +87,6 @@ struct mox_rwtm { u64 serial_number; int board_version, ram_size; u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN]; - - bool has_pubkey; - u8 pubkey[135]; - -#ifdef CONFIG_DEBUG_FS - /* - * Signature process. This is currently done via debugfs, because it - * does not conform to the sysfs standard "one file per attribute". - * It should be rewritten via crypto API once akcipher API is available - * from userspace. - */ - u32 last_sig[MOX_ECC_SIGNATURE_WORDS]; - bool last_sig_done; -#endif }; static inline struct device *rwtm_dev(struct mox_rwtm *rwtm) @@ -120,24 +94,23 @@ static inline struct device *rwtm_dev(struct mox_rwtm *rwtm) return rwtm->mbox_client.dev; } -#define MOX_ATTR_RO(name, format, cat) \ +#define MOX_ATTR_RO(name, format) \ static ssize_t \ name##_show(struct device *dev, struct device_attribute *a, \ char *buf) \ { \ struct mox_rwtm *rwtm = dev_get_drvdata(dev); \ - if (!rwtm->has_##cat) \ + if (!rwtm->has_board_info) \ return -ENODATA; \ return sysfs_emit(buf, format, rwtm->name); \ } \ static DEVICE_ATTR_RO(name) -MOX_ATTR_RO(serial_number, "%016llX\n", board_info); -MOX_ATTR_RO(board_version, "%i\n", board_info); -MOX_ATTR_RO(ram_size, "%i\n", board_info); -MOX_ATTR_RO(mac_address1, "%pM\n", board_info); -MOX_ATTR_RO(mac_address2, "%pM\n", board_info); -MOX_ATTR_RO(pubkey, "%s\n", pubkey); +MOX_ATTR_RO(serial_number, "%016llX\n"); +MOX_ATTR_RO(board_version, "%i\n"); +MOX_ATTR_RO(ram_size, "%i\n"); +MOX_ATTR_RO(mac_address1, "%pM\n"); +MOX_ATTR_RO(mac_address2, "%pM\n"); static struct attribute *turris_mox_rwtm_attrs[] = { &dev_attr_serial_number.attr, @@ -145,7 +118,6 @@ static struct attribute *turris_mox_rwtm_attrs[] = { &dev_attr_ram_size.attr, &dev_attr_mac_address1.attr, &dev_attr_mac_address2.attr, - &dev_attr_pubkey.attr, NULL }; ATTRIBUTE_GROUPS(turris_mox_rwtm); @@ -247,24 +219,6 @@ static int mox_get_board_info(struct mox_rwtm *rwtm) pr_info(" burned RAM size %i MiB\n", rwtm->ram_size); } - ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false); - if (ret == -ENODATA) { - dev_warn(dev, "Board has no public key burned!\n"); - } else if (ret == -EOPNOTSUPP) { - dev_notice(dev, - "Firmware does not support the ECDSA_PUB_KEY command\n"); - } else if (ret < 0) { - return ret; - } else { - u32 *s = reply->status; - - rwtm->has_pubkey = true; - sprintf(rwtm->pubkey, - "%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", - ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], - s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); - } - return 0; } @@ -306,128 +260,6 @@ static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) return ret; } -#ifdef CONFIG_DEBUG_FS -static int rwtm_debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return nonseekable_open(inode, file); -} - -static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len, - loff_t *ppos) -{ - struct mox_rwtm *rwtm = file->private_data; - ssize_t ret; - - /* only allow one read, of whole signature, from position 0 */ - if (*ppos != 0) - return 0; - - if (len < sizeof(rwtm->last_sig)) - return -EINVAL; - - if (!rwtm->last_sig_done) - return -ENODATA; - - ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, - sizeof(rwtm->last_sig)); - rwtm->last_sig_done = false; - - return ret; -} - -static ssize_t do_sign_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - struct mox_rwtm *rwtm = file->private_data; - struct armada_37xx_rwtm_tx_msg msg; - loff_t dummy = 0; - ssize_t ret; - - if (len != SHA512_DIGEST_SIZE) - return -EINVAL; - - /* if last result is not zero user has not read that information yet */ - if (rwtm->last_sig_done) - return -EBUSY; - - if (!mutex_trylock(&rwtm->busy)) - return -EBUSY; - - /* - * Here we have to send: - * 1. Address of the input to sign. - * The input is an array of 17 32-bit words, the first (most - * significat) is 0, the rest 16 words are copied from the SHA-512 - * hash given by the user and converted from BE to LE. - * 2. Address of the buffer where ECDSA signature value R shall be - * stored by the rWTM firmware. - * 3. Address of the buffer where ECDSA signature value S shall be - * stored by the rWTM firmware. - */ - memset(rwtm->buf, 0, sizeof(u32)); - ret = simple_write_to_buffer(rwtm->buf + sizeof(u32), - SHA512_DIGEST_SIZE, &dummy, buf, len); - if (ret < 0) - goto unlock_mutex; - be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUMBER_WORDS); - - msg.args[0] = 1; - msg.args[1] = rwtm->buf_phys; - msg.args[2] = rwtm->buf_phys + MOX_ECC_NUMBER_LEN; - msg.args[3] = rwtm->buf_phys + 2 * MOX_ECC_NUMBER_LEN; - - ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true); - if (ret < 0) - goto unlock_mutex; - - /* - * Here we read the R and S values of the ECDSA signature - * computed by the rWTM firmware and convert their words from - * LE to BE. - */ - memcpy(rwtm->last_sig, rwtm->buf + MOX_ECC_NUMBER_LEN, - sizeof(rwtm->last_sig)); - cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, - MOX_ECC_SIGNATURE_WORDS); - rwtm->last_sig_done = true; - - mutex_unlock(&rwtm->busy); - return len; -unlock_mutex: - mutex_unlock(&rwtm->busy); - return ret; -} - -static const struct file_operations do_sign_fops = { - .owner = THIS_MODULE, - .open = rwtm_debug_open, - .read = do_sign_read, - .write = do_sign_write, -}; - -static void rwtm_debugfs_release(void *root) -{ - debugfs_remove_recursive(root); -} - -static void rwtm_register_debugfs(struct mox_rwtm *rwtm) -{ - struct dentry *root; - - root = debugfs_create_dir("turris-mox-rwtm", NULL); - - debugfs_create_file_unsafe("do_sign", 0600, root, rwtm, &do_sign_fops); - - devm_add_action_or_reset(rwtm_dev(rwtm), rwtm_debugfs_release, root); -} -#else -static inline void rwtm_register_debugfs(struct mox_rwtm *rwtm) -{ -} -#endif - static void rwtm_devm_mbox_release(void *mbox) { mbox_free_channel(mbox); @@ -491,8 +323,6 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "Cannot register HWRNG!\n"); - rwtm_register_debugfs(rwtm); - dev_info(dev, "HWRNG successfully registered\n"); /* From patchwork Tue Feb 4 13:14:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 13959181 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 000D21DFEF for ; Tue, 4 Feb 2025 13:14:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674894; cv=none; b=S1VxyTXgmhk0SfSqUbcV1m1qtxutv+4kB1cmMFL2HGsFNsYp1/E3H1pP3WZ8ufKc2tR7AsCq+Hss6rSB9uSYQOqzcE1Emg20xvvjmxht/E+MKL0unJKdNJ/AYaU08MDxB4WzPU7/fUZnHqTPz2RW0QhKTjwPsgGkbpLJSnCHr/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738674894; c=relaxed/simple; bh=B7DAB56Xm3D1cfn4h+ZqUxWnsqL3AZaInKJCIgVdlTA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Tm7LwtYBsBnxn4/M8yiGBb2ElLB1g1jSVR7by6egysK57+PZHjOSpz5wRQrSdPwuVcAxZWhJ2lMrZyd+c1NRW84v2Y+5r65xObdwbzOgL0AFnCH802s6Pcf3AwR6cjym1NwdBQzHs054x6AzQvu0wMnoAkq6bVud+c9+Yp+0hD0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Z2bYabCh; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Z2bYabCh" Received: by smtp.kernel.org (Postfix) id EB0F6C4CEE4; Tue, 4 Feb 2025 13:14:53 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A7A92C4CEE2; Tue, 4 Feb 2025 13:14:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738674893; bh=B7DAB56Xm3D1cfn4h+ZqUxWnsqL3AZaInKJCIgVdlTA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z2bYabCh0pHHTXgar8sgn7NbpjLWrU6lt3FxW9ROgX1a+Cwp2441ZxQyWcZXfzE12 nlEWIiMjahfRrzlQ6uijjFp+WKns9OGMZAk/ips2UPMHNm3jwpggvRaZqQav4nv6NB bi6mJVF0r5UUbXEPNgq1vOkyE2FZBYS4APhJ5Ar36dBS24IqhiY46+h836wtF0d5uR lS8Gs99PAdoExIz22MemNcP6+V1AWct9uaOasARo1knm7TvO4zewYkk1+ojEPbiiR0 xuYeHFHcSUmx/p1xY/8TgszSNzjzU0XcaxzSRWdQctpc4V2yNMAIM+941pST+cxIyd 266ZjynlMEGDA== From: =?utf-8?q?Marek_Beh=C3=BAn?= To: Arnd Bergmann , soc@kernel.org Cc: arm@kernel.org, Andy Shevchenko , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , linux-crypto@vger.kernel.org, Herbert Xu , Greg Kroah-Hartman , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH 5/5] firmware: turris-mox-rwtm: Add support for ECDSA signatures with HW private key Date: Tue, 4 Feb 2025 14:14:15 +0100 Message-ID: <20250204131415.27014-6-kabel@kernel.org> X-Mailer: git-send-email 2.45.3 In-Reply-To: <20250204131415.27014-1-kabel@kernel.org> References: <20250204131415.27014-1-kabel@kernel.org> Precedence: bulk X-Mailing-List: soc@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add support for digital message signing with the private key stored in the rWTM secure coprocessor. Turris Mox devices have an ECDSA private key generated and burned into rWTM eFuses when manufactured. This private key is not readable from the rWTM, but rWTM firmware allows for signing messages with it and retrieving the public key. This is exposed to userspace via the keyctl API. User can find the key by either looking at /proc/keys or listing the keyring: $ cat /proc/keys 0240b221 ... keyring .turris-signing-keys: 1 34ff9ac9 ... turris-si Turris MOX SN 0000000D30000005 rWTM ECDSA ke... $ keyctl rlist %:.turris-signing-keys 889166537 To get the public key: $ keyctl read 889166537 67 bytes of data in key: 0201a05c 1a79242b 13f2fc02 b48ffdbb 6ee8d5ba 812d6784 5f04f302 c0894d3e b93474f9 46235777 5c926fb4 cce89b50 88cf5d10 c07fd9c5 fdcea257 3d8f1c33 1bf826 To sign a message: $ dd if=/dev/urandom of=msg_to_sign bs=64 count=1 $ keyctl pkey_sign 889166537 0 msg_to_sign >signature Signed-off-by: Marek Behún --- drivers/firmware/Kconfig | 17 ++++ drivers/firmware/turris-mox-rwtm.c | 158 ++++++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 71d8b26c4103..9eaef7da2a56 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -257,6 +257,23 @@ config TURRIS_MOX_RWTM other manufacturing data and also utilize the Entropy Bit Generator for hardware random number generation. +if TURRIS_MOX_RWTM + +config TURRIS_MOX_RWTM_KEYCTL + bool "Turris Mox rWTM ECDSA message signing" + default y + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + select CZNIC_PLATFORMS + select TURRIS_SIGNING_KEY + help + Say Y here to add support for ECDSA message signing with board private + key (each Turris Mox has an ECDSA private key generated in the secure + coprocessor when manufactured). This functionality is exposed via the + keyctl() syscall. + +endif # TURRIS_MOX_RWTM + source "drivers/firmware/arm_ffa/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/cirrus/Kconfig" diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index 16e5f19dfafd..1eac9948148f 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -2,11 +2,13 @@ /* * Turris Mox rWTM firmware driver * - * Copyright (C) 2019, 2024 Marek Behún + * Copyright (C) 2019, 2024, 2025 Marek Behún */ +#include #include #include +#include #include #include #include @@ -14,14 +16,17 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include #define DRIVER_NAME "turris-mox-rwtm" @@ -34,6 +39,14 @@ * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. */ +enum { + MOX_ECC_NUM_BITS = 521, + MOX_ECC_NUM_LEN = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 8), + MOX_ECC_NUM_WORDS = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 32), + MOX_ECC_SIG_LEN = 2 * MOX_ECC_NUM_LEN, + MOX_ECC_PUBKEY_LEN = 1 + MOX_ECC_NUM_LEN, +}; + #define MBOX_STS_SUCCESS (0 << 30) #define MBOX_STS_FAIL (1 << 30) #define MBOX_STS_BADCMD (2 << 30) @@ -69,6 +82,7 @@ enum mbox_cmd { * @ram_size: RAM size of the device * @mac_address1: first MAC address of the device * @mac_address2: second MAC address of the device + * @pubkey: board ECDSA public key */ struct mox_rwtm { struct mbox_client mbox_client; @@ -87,6 +101,10 @@ struct mox_rwtm { u64 serial_number; int board_version, ram_size; u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN]; + +#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL + u8 pubkey[MOX_ECC_PUBKEY_LEN]; +#endif }; static inline struct device *rwtm_dev(struct mox_rwtm *rwtm) @@ -260,6 +278,140 @@ static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) return ret; } +#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL + +static void mox_ecc_number_to_bin(void *dst, const u32 *src) +{ + __be32 tmp[MOX_ECC_NUM_WORDS]; + + cpu_to_be32_array(tmp, src, MOX_ECC_NUM_WORDS); + + memcpy(dst, (void *)tmp + 2, MOX_ECC_NUM_LEN); +} + +static void mox_ecc_public_key_to_bin(void *dst, u32 src_first, + const u32 *src_rest) +{ + __be32 tmp[MOX_ECC_NUM_WORDS - 1]; + u8 *p = dst; + + /* take 3 bytes from the first word */ + *p++ = src_first >> 16; + *p++ = src_first >> 8; + *p++ = src_first; + + /* take the rest of the words */ + cpu_to_be32_array(tmp, src_rest, MOX_ECC_NUM_WORDS - 1); + memcpy(p, tmp, sizeof(tmp)); +} + +static int mox_rwtm_sign(const struct key *key, const void *data, void *signature) +{ + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); + struct armada_37xx_rwtm_tx_msg msg = {}; + u32 offset_r, offset_s; + int ret; + + guard(mutex)(&rwtm->busy); + + /* + * For MBOX_CMD_SIGN command: + * args[0] - must be 1 + * args[1] - address of message M to sign; message is a 521-bit number + * args[2] - address where the R part of the signature will be stored + * args[3] - address where the S part of the signature will be stored + * + * M, R and S are 521-bit numbers encoded as seventeen 32-bit words, + * most significat word first. + * Since the message in @data is a sha512 digest, the most significat + * word is always zero. + */ + + offset_r = MOX_ECC_NUM_WORDS * sizeof(u32); + offset_s = 2 * MOX_ECC_NUM_WORDS * sizeof(u32); + + memset(rwtm->buf, 0, sizeof(u32)); + memcpy(rwtm->buf + sizeof(u32), data, SHA512_DIGEST_SIZE); + be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUM_WORDS); + + msg.args[0] = 1; + msg.args[1] = rwtm->buf_phys; + msg.args[2] = rwtm->buf_phys + offset_r; + msg.args[3] = rwtm->buf_phys + offset_s; + + ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true); + if (ret < 0) + return ret; + + /* convert R and S parts of the signature */ + mox_ecc_number_to_bin(signature, rwtm->buf + offset_r); + mox_ecc_number_to_bin(signature + MOX_ECC_NUM_LEN, rwtm->buf + offset_s); + + return 0; +} + +static const void *mox_rwtm_get_public_key(const struct key *key) +{ + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); + + return rwtm->pubkey; +} + +static const struct turris_signing_key_subtype mox_signing_key_subtype = { + .key_size = MOX_ECC_NUM_BITS, + .data_size = SHA512_DIGEST_SIZE, + .sig_size = MOX_ECC_SIG_LEN, + .public_key_size = MOX_ECC_PUBKEY_LEN, + .hash_algo = "sha512", + .get_public_key = mox_rwtm_get_public_key, + .sign = mox_rwtm_sign, +}; + +static int mox_register_signing_key(struct mox_rwtm *rwtm) +{ + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; + struct device *dev = rwtm_dev(rwtm); + int ret; + + ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false); + if (ret == -ENODATA) { + dev_warn(dev, "Board has no public key burned!\n"); + } else if (ret == -EOPNOTSUPP) { + dev_notice(dev, + "Firmware does not support the ECDSA_PUB_KEY command\n"); + } else if (ret < 0) { + return ret; + } else { + char sn[17] = "unknown"; + char desc[46]; + + if (rwtm->has_board_info) + sprintf(sn, "%016llX", rwtm->serial_number); + + sprintf(desc, "Turris MOX SN %s rWTM ECDSA key", sn); + + mox_ecc_public_key_to_bin(rwtm->pubkey, ret, reply->status); + + ret = devm_turris_signing_key_create(dev, + &mox_signing_key_subtype, + desc); + if (ret) + return dev_err_probe(dev, ret, + "Cannot create signing key\n"); + } + + return 0; +} + +#else /* CONFIG_TURRIS_MOX_RWTM_KEYCTL */ + +static inline int mox_register_signing_key(struct mox_rwtm *rwtm) +{ + return 0; +} + +#endif /* !CONFIG_TURRIS_MOX_RWTM_KEYCTL */ + static void rwtm_devm_mbox_release(void *mbox) { mbox_free_channel(mbox); @@ -309,6 +461,10 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) if (ret < 0) dev_warn(dev, "Cannot read board information: %i\n", ret); + ret = mox_register_signing_key(rwtm); + if (ret < 0) + return ret; + ret = check_get_random_support(rwtm); if (ret < 0) { dev_notice(dev,