mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
163 lines
3.8 KiB
C
163 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;
|
||
|
}
|