mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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 <kabel@kernel.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
162 lines
3.8 KiB
C
162 lines
3.8 KiB
C
// 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;
|
|
}
|