linux/drivers/platform/cznic/turris-omnia-mcu-keyctl.c

163 lines
3.8 KiB
C
Raw Permalink Normal View History

// 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;
}