646 lines
20 KiB
C
646 lines
20 KiB
C
|
/*
|
||
|
* QEMU Cryptodev backend for QEMU cipher APIs
|
||
|
*
|
||
|
* Copyright (c) 2022 Bytedance.Inc
|
||
|
*
|
||
|
* Authors:
|
||
|
* lei he <helei.sig11@bytedance.com>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "qemu/osdep.h"
|
||
|
#include "crypto/cipher.h"
|
||
|
#include "crypto/akcipher.h"
|
||
|
#include "qapi/error.h"
|
||
|
#include "qemu/main-loop.h"
|
||
|
#include "qemu/thread.h"
|
||
|
#include "qemu/error-report.h"
|
||
|
#include "qemu/queue.h"
|
||
|
#include "qom/object.h"
|
||
|
#include "sysemu/cryptodev.h"
|
||
|
#include "standard-headers/linux/virtio_crypto.h"
|
||
|
|
||
|
#include <keyutils.h>
|
||
|
#include <sys/eventfd.h>
|
||
|
|
||
|
/**
|
||
|
* @TYPE_CRYPTODEV_BACKEND_LKCF:
|
||
|
* name of backend that uses linux kernel crypto framework
|
||
|
*/
|
||
|
#define TYPE_CRYPTODEV_BACKEND_LKCF "cryptodev-backend-lkcf"
|
||
|
|
||
|
OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendLKCF, CRYPTODEV_BACKEND_LKCF)
|
||
|
|
||
|
#define INVALID_KEY_ID -1
|
||
|
#define MAX_SESSIONS 256
|
||
|
#define NR_WORKER_THREAD 64
|
||
|
|
||
|
#define KCTL_KEY_TYPE_PKEY "asymmetric"
|
||
|
/**
|
||
|
* Here the key is uploaded to the thread-keyring of worker thread, at least
|
||
|
* util linux-6.0:
|
||
|
* 1. process keyring seems to behave unexpectedly if main-thread does not
|
||
|
* create the keyring before creating any other thread.
|
||
|
* 2. at present, the guest kernel never perform multiple operations on a
|
||
|
* session.
|
||
|
* 3. it can reduce the load of the main-loop because the key passed by the
|
||
|
* guest kernel has been already checked.
|
||
|
*/
|
||
|
#define KCTL_KEY_RING KEY_SPEC_THREAD_KEYRING
|
||
|
|
||
|
typedef struct CryptoDevBackendLKCFSession {
|
||
|
uint8_t *key;
|
||
|
size_t keylen;
|
||
|
QCryptoAkCipherKeyType keytype;
|
||
|
QCryptoAkCipherOptions akcipher_opts;
|
||
|
} CryptoDevBackendLKCFSession;
|
||
|
|
||
|
typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF;
|
||
|
typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
|
||
|
struct CryptoDevLKCFTask {
|
||
|
CryptoDevBackendLKCFSession *sess;
|
||
|
CryptoDevBackendOpInfo *op_info;
|
||
|
CryptoDevCompletionFunc cb;
|
||
|
void *opaque;
|
||
|
int status;
|
||
|
CryptoDevBackendLKCF *lkcf;
|
||
|
QSIMPLEQ_ENTRY(CryptoDevLKCFTask) queue;
|
||
|
};
|
||
|
|
||
|
typedef struct CryptoDevBackendLKCF {
|
||
|
CryptoDevBackend parent_obj;
|
||
|
CryptoDevBackendLKCFSession *sess[MAX_SESSIONS];
|
||
|
QSIMPLEQ_HEAD(, CryptoDevLKCFTask) requests;
|
||
|
QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
|
||
|
QemuMutex mutex;
|
||
|
QemuCond cond;
|
||
|
QemuMutex rsp_mutex;
|
||
|
|
||
|
/**
|
||
|
* There is no async interface for asymmetric keys like AF_ALG sockets,
|
||
|
* we don't seem to have better way than create a lots of thread.
|
||
|
*/
|
||
|
QemuThread worker_threads[NR_WORKER_THREAD];
|
||
|
bool running;
|
||
|
int eventfd;
|
||
|
} CryptoDevBackendLKCF;
|
||
|
|
||
|
static void *cryptodev_lkcf_worker(void *arg);
|
||
|
static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
|
||
|
uint64_t session_id,
|
||
|
uint32_t queue_index,
|
||
|
CryptoDevCompletionFunc cb,
|
||
|
void *opaque);
|
||
|
|
||
|
static void cryptodev_lkcf_handle_response(void *opaque)
|
||
|
{
|
||
|
CryptoDevBackendLKCF *lkcf = (CryptoDevBackendLKCF *)opaque;
|
||
|
QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
|
||
|
CryptoDevLKCFTask *task, *next;
|
||
|
eventfd_t nevent;
|
||
|
|
||
|
QSIMPLEQ_INIT(&responses);
|
||
|
eventfd_read(lkcf->eventfd, &nevent);
|
||
|
|
||
|
qemu_mutex_lock(&lkcf->rsp_mutex);
|
||
|
QSIMPLEQ_PREPEND(&responses, &lkcf->responses);
|
||
|
qemu_mutex_unlock(&lkcf->rsp_mutex);
|
||
|
|
||
|
QSIMPLEQ_FOREACH_SAFE(task, &responses, queue, next) {
|
||
|
if (task->cb) {
|
||
|
task->cb(task->opaque, task->status);
|
||
|
}
|
||
|
g_free(task);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions *opts,
|
||
|
char *key_desc,
|
||
|
size_t desc_len,
|
||
|
Error **errp)
|
||
|
{
|
||
|
QCryptoAkCipherOptionsRSA *rsa_opt;
|
||
|
if (opts->alg != QCRYPTO_AKCIPHER_ALG_RSA) {
|
||
|
error_setg(errp, "Unsupported alg: %u", opts->alg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
rsa_opt = &opts->u.rsa;
|
||
|
if (rsa_opt->padding_alg == QCRYPTO_RSA_PADDING_ALG_PKCS1) {
|
||
|
snprintf(key_desc, desc_len, "enc=%s hash=%s",
|
||
|
QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg),
|
||
|
QCryptoHashAlgorithm_str(rsa_opt->hash_alg));
|
||
|
|
||
|
} else {
|
||
|
snprintf(key_desc, desc_len, "enc=%s",
|
||
|
QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg));
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg,
|
||
|
int virtio_hash_alg,
|
||
|
QCryptoAkCipherOptionsRSA *opt,
|
||
|
Error **errp)
|
||
|
{
|
||
|
if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
|
||
|
opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
|
||
|
|
||
|
switch (virtio_hash_alg) {
|
||
|
case VIRTIO_CRYPTO_RSA_MD5:
|
||
|
opt->hash_alg = QCRYPTO_HASH_ALG_MD5;
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_RSA_SHA1:
|
||
|
opt->hash_alg = QCRYPTO_HASH_ALG_SHA1;
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_RSA_SHA256:
|
||
|
opt->hash_alg = QCRYPTO_HASH_ALG_SHA256;
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_RSA_SHA512:
|
||
|
opt->hash_alg = QCRYPTO_HASH_ALG_SHA512;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error_setg(errp, "Unsupported rsa hash algo: %d", virtio_hash_alg);
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
|
||
|
opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
error_setg(errp, "Unsupported rsa padding algo: %u", virtio_padding_alg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_get_unused_session_index(CryptoDevBackendLKCF *lkcf)
|
||
|
{
|
||
|
size_t i;
|
||
|
|
||
|
for (i = 0; i < MAX_SESSIONS; i++) {
|
||
|
if (lkcf->sess[i] == NULL) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp)
|
||
|
{
|
||
|
/* Only support one queue */
|
||
|
int queues = backend->conf.peers.queues, i;
|
||
|
CryptoDevBackendClient *cc;
|
||
|
CryptoDevBackendLKCF *lkcf =
|
||
|
CRYPTODEV_BACKEND_LKCF(backend);
|
||
|
|
||
|
if (queues != 1) {
|
||
|
error_setg(errp,
|
||
|
"Only support one queue in cryptodev-builtin backend");
|
||
|
return;
|
||
|
}
|
||
|
lkcf->eventfd = eventfd(0, 0);
|
||
|
if (lkcf->eventfd < 0) {
|
||
|
error_setg(errp, "Failed to create eventfd: %d", errno);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cc = cryptodev_backend_new_client("cryptodev-lkcf", NULL);
|
||
|
cc->info_str = g_strdup_printf("cryptodev-lkcf0");
|
||
|
cc->queue_index = 0;
|
||
|
cc->type = CRYPTODEV_BACKEND_TYPE_LKCF;
|
||
|
backend->conf.peers.ccs[0] = cc;
|
||
|
|
||
|
backend->conf.crypto_services =
|
||
|
1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
|
||
|
backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
|
||
|
lkcf->running = true;
|
||
|
|
||
|
QSIMPLEQ_INIT(&lkcf->requests);
|
||
|
QSIMPLEQ_INIT(&lkcf->responses);
|
||
|
qemu_mutex_init(&lkcf->mutex);
|
||
|
qemu_mutex_init(&lkcf->rsp_mutex);
|
||
|
qemu_cond_init(&lkcf->cond);
|
||
|
for (i = 0; i < NR_WORKER_THREAD; i++) {
|
||
|
qemu_thread_create(&lkcf->worker_threads[i], "lkcf-worker",
|
||
|
cryptodev_lkcf_worker, lkcf, 0);
|
||
|
}
|
||
|
qemu_set_fd_handler(
|
||
|
lkcf->eventfd, cryptodev_lkcf_handle_response, NULL, lkcf);
|
||
|
cryptodev_backend_set_ready(backend, true);
|
||
|
}
|
||
|
|
||
|
static void cryptodev_lkcf_cleanup(CryptoDevBackend *backend, Error **errp)
|
||
|
{
|
||
|
CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
|
||
|
size_t i;
|
||
|
int queues = backend->conf.peers.queues;
|
||
|
CryptoDevBackendClient *cc;
|
||
|
CryptoDevLKCFTask *task, *next;
|
||
|
|
||
|
qemu_mutex_lock(&lkcf->mutex);
|
||
|
lkcf->running = false;
|
||
|
qemu_mutex_unlock(&lkcf->mutex);
|
||
|
qemu_cond_broadcast(&lkcf->cond);
|
||
|
|
||
|
close(lkcf->eventfd);
|
||
|
for (i = 0; i < NR_WORKER_THREAD; i++) {
|
||
|
qemu_thread_join(&lkcf->worker_threads[i]);
|
||
|
}
|
||
|
|
||
|
QSIMPLEQ_FOREACH_SAFE(task, &lkcf->requests, queue, next) {
|
||
|
if (task->cb) {
|
||
|
task->cb(task->opaque, task->status);
|
||
|
}
|
||
|
g_free(task);
|
||
|
}
|
||
|
|
||
|
QSIMPLEQ_FOREACH_SAFE(task, &lkcf->responses, queue, next) {
|
||
|
if (task->cb) {
|
||
|
task->cb(task->opaque, task->status);
|
||
|
}
|
||
|
g_free(task);
|
||
|
}
|
||
|
|
||
|
qemu_mutex_destroy(&lkcf->mutex);
|
||
|
qemu_cond_destroy(&lkcf->cond);
|
||
|
qemu_mutex_destroy(&lkcf->rsp_mutex);
|
||
|
|
||
|
for (i = 0; i < MAX_SESSIONS; i++) {
|
||
|
if (lkcf->sess[i] != NULL) {
|
||
|
cryptodev_lkcf_close_session(backend, i, 0, NULL, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < queues; i++) {
|
||
|
cc = backend->conf.peers.ccs[i];
|
||
|
if (cc) {
|
||
|
cryptodev_backend_free_client(cc);
|
||
|
backend->conf.peers.ccs[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cryptodev_backend_set_ready(backend, false);
|
||
|
}
|
||
|
|
||
|
static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task)
|
||
|
{
|
||
|
CryptoDevBackendLKCFSession *session = task->sess;
|
||
|
CryptoDevBackendAsymOpInfo *asym_op_info;
|
||
|
bool kick = false;
|
||
|
int ret, status, op_code = task->op_info->op_code;
|
||
|
size_t p8info_len;
|
||
|
g_autofree uint8_t *p8info = NULL;
|
||
|
Error *local_error = NULL;
|
||
|
key_serial_t key_id = INVALID_KEY_ID;
|
||
|
char op_desc[64];
|
||
|
g_autoptr(QCryptoAkCipher) akcipher = NULL;
|
||
|
|
||
|
/**
|
||
|
* We only offload private key session:
|
||
|
* 1. currently, the Linux kernel can only accept public key wrapped
|
||
|
* with X.509 certificates, but unfortunately the cost of making a
|
||
|
* ceritificate with public key is too expensive.
|
||
|
* 2. generally, public key related compution is fast, just compute it with
|
||
|
* thread-pool.
|
||
|
*/
|
||
|
if (session->keytype == QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE) {
|
||
|
if (qcrypto_akcipher_export_p8info(&session->akcipher_opts,
|
||
|
session->key, session->keylen,
|
||
|
&p8info, &p8info_len,
|
||
|
&local_error) != 0 ||
|
||
|
cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc,
|
||
|
sizeof(op_desc), &local_error) != 0) {
|
||
|
error_report_err(local_error);
|
||
|
} else {
|
||
|
key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key",
|
||
|
p8info, p8info_len, KCTL_KEY_RING);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (key_id < 0) {
|
||
|
if (!qcrypto_akcipher_supports(&session->akcipher_opts)) {
|
||
|
status = -VIRTIO_CRYPTO_NOTSUPP;
|
||
|
goto out;
|
||
|
}
|
||
|
akcipher = qcrypto_akcipher_new(&session->akcipher_opts,
|
||
|
session->keytype,
|
||
|
session->key, session->keylen,
|
||
|
&local_error);
|
||
|
if (!akcipher) {
|
||
|
status = -VIRTIO_CRYPTO_ERR;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
asym_op_info = task->op_info->u.asym_op_info;
|
||
|
switch (op_code) {
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
|
||
|
if (key_id >= 0) {
|
||
|
ret = keyctl_pkey_encrypt(key_id, op_desc,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len);
|
||
|
} else {
|
||
|
ret = qcrypto_akcipher_encrypt(akcipher,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len, &local_error);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
|
||
|
if (key_id >= 0) {
|
||
|
ret = keyctl_pkey_decrypt(key_id, op_desc,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len);
|
||
|
} else {
|
||
|
ret = qcrypto_akcipher_decrypt(akcipher,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len, &local_error);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_SIGN:
|
||
|
if (key_id >= 0) {
|
||
|
ret = keyctl_pkey_sign(key_id, op_desc,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len);
|
||
|
} else {
|
||
|
ret = qcrypto_akcipher_sign(akcipher,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len, &local_error);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
|
||
|
if (key_id >= 0) {
|
||
|
ret = keyctl_pkey_verify(key_id, op_desc,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len);
|
||
|
} else {
|
||
|
ret = qcrypto_akcipher_verify(akcipher,
|
||
|
asym_op_info->src, asym_op_info->src_len,
|
||
|
asym_op_info->dst, asym_op_info->dst_len, &local_error);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error_setg(&local_error, "Unknown opcode: %u", op_code);
|
||
|
status = -VIRTIO_CRYPTO_ERR;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (ret < 0) {
|
||
|
if (!local_error) {
|
||
|
if (errno != EKEYREJECTED) {
|
||
|
error_report("Failed do operation with keyctl: %d", errno);
|
||
|
}
|
||
|
} else {
|
||
|
error_report_err(local_error);
|
||
|
}
|
||
|
status = op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY ?
|
||
|
-VIRTIO_CRYPTO_KEY_REJECTED : -VIRTIO_CRYPTO_ERR;
|
||
|
} else {
|
||
|
status = VIRTIO_CRYPTO_OK;
|
||
|
asym_op_info->dst_len = ret;
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
if (key_id >= 0) {
|
||
|
keyctl_unlink(key_id, KCTL_KEY_RING);
|
||
|
}
|
||
|
task->status = status;
|
||
|
|
||
|
qemu_mutex_lock(&task->lkcf->rsp_mutex);
|
||
|
if (QSIMPLEQ_EMPTY(&task->lkcf->responses)) {
|
||
|
kick = true;
|
||
|
}
|
||
|
QSIMPLEQ_INSERT_TAIL(&task->lkcf->responses, task, queue);
|
||
|
qemu_mutex_unlock(&task->lkcf->rsp_mutex);
|
||
|
|
||
|
if (kick) {
|
||
|
eventfd_write(task->lkcf->eventfd, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void *cryptodev_lkcf_worker(void *arg)
|
||
|
{
|
||
|
CryptoDevBackendLKCF *backend = (CryptoDevBackendLKCF *)arg;
|
||
|
CryptoDevLKCFTask *task;
|
||
|
|
||
|
for (;;) {
|
||
|
task = NULL;
|
||
|
qemu_mutex_lock(&backend->mutex);
|
||
|
while (backend->running && QSIMPLEQ_EMPTY(&backend->requests)) {
|
||
|
qemu_cond_wait(&backend->cond, &backend->mutex);
|
||
|
}
|
||
|
if (backend->running) {
|
||
|
task = QSIMPLEQ_FIRST(&backend->requests);
|
||
|
QSIMPLEQ_REMOVE_HEAD(&backend->requests, queue);
|
||
|
}
|
||
|
qemu_mutex_unlock(&backend->mutex);
|
||
|
|
||
|
/* stopped */
|
||
|
if (!task) {
|
||
|
break;
|
||
|
}
|
||
|
cryptodev_lkcf_execute_task(task);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_operation(
|
||
|
CryptoDevBackend *backend,
|
||
|
CryptoDevBackendOpInfo *op_info,
|
||
|
uint32_t queue_index,
|
||
|
CryptoDevCompletionFunc cb,
|
||
|
void *opaque)
|
||
|
{
|
||
|
CryptoDevBackendLKCF *lkcf =
|
||
|
CRYPTODEV_BACKEND_LKCF(backend);
|
||
|
CryptoDevBackendLKCFSession *sess;
|
||
|
enum CryptoDevBackendAlgType algtype = op_info->algtype;
|
||
|
CryptoDevLKCFTask *task;
|
||
|
|
||
|
if (op_info->session_id >= MAX_SESSIONS ||
|
||
|
lkcf->sess[op_info->session_id] == NULL) {
|
||
|
error_report("Cannot find a valid session id: %" PRIu64 "",
|
||
|
op_info->session_id);
|
||
|
return -VIRTIO_CRYPTO_INVSESS;
|
||
|
}
|
||
|
|
||
|
sess = lkcf->sess[op_info->session_id];
|
||
|
if (algtype != CRYPTODEV_BACKEND_ALG_ASYM) {
|
||
|
error_report("algtype not supported: %u", algtype);
|
||
|
return -VIRTIO_CRYPTO_NOTSUPP;
|
||
|
}
|
||
|
|
||
|
task = g_new0(CryptoDevLKCFTask, 1);
|
||
|
task->op_info = op_info;
|
||
|
task->cb = cb;
|
||
|
task->opaque = opaque;
|
||
|
task->sess = sess;
|
||
|
task->lkcf = lkcf;
|
||
|
task->status = -VIRTIO_CRYPTO_ERR;
|
||
|
|
||
|
qemu_mutex_lock(&lkcf->mutex);
|
||
|
QSIMPLEQ_INSERT_TAIL(&lkcf->requests, task, queue);
|
||
|
qemu_mutex_unlock(&lkcf->mutex);
|
||
|
qemu_cond_signal(&lkcf->cond);
|
||
|
|
||
|
return VIRTIO_CRYPTO_OK;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_create_asym_session(
|
||
|
CryptoDevBackendLKCF *lkcf,
|
||
|
CryptoDevBackendAsymSessionInfo *sess_info,
|
||
|
uint64_t *session_id)
|
||
|
{
|
||
|
Error *local_error = NULL;
|
||
|
int index;
|
||
|
g_autofree CryptoDevBackendLKCFSession *sess =
|
||
|
g_new0(CryptoDevBackendLKCFSession, 1);
|
||
|
|
||
|
switch (sess_info->algo) {
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_RSA:
|
||
|
sess->akcipher_opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
|
||
|
if (cryptodev_lkcf_set_rsa_opt(
|
||
|
sess_info->u.rsa.padding_algo, sess_info->u.rsa.hash_algo,
|
||
|
&sess->akcipher_opts.u.rsa, &local_error) != 0) {
|
||
|
error_report_err(local_error);
|
||
|
return -VIRTIO_CRYPTO_ERR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error_report("Unsupported asym alg %u", sess_info->algo);
|
||
|
return -VIRTIO_CRYPTO_NOTSUPP;
|
||
|
}
|
||
|
|
||
|
switch (sess_info->keytype) {
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
|
||
|
sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
|
||
|
break;
|
||
|
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
|
||
|
sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
error_report("Unknown akcipher keytype: %u", sess_info->keytype);
|
||
|
return -VIRTIO_CRYPTO_ERR;
|
||
|
}
|
||
|
|
||
|
index = cryptodev_lkcf_get_unused_session_index(lkcf);
|
||
|
if (index < 0) {
|
||
|
error_report("Total number of sessions created exceeds %u",
|
||
|
MAX_SESSIONS);
|
||
|
return -VIRTIO_CRYPTO_ERR;
|
||
|
}
|
||
|
|
||
|
sess->keylen = sess_info->keylen;
|
||
|
sess->key = g_malloc(sess_info->keylen);
|
||
|
memcpy(sess->key, sess_info->key, sess_info->keylen);
|
||
|
|
||
|
lkcf->sess[index] = g_steal_pointer(&sess);
|
||
|
*session_id = index;
|
||
|
|
||
|
return VIRTIO_CRYPTO_OK;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_create_session(
|
||
|
CryptoDevBackend *backend,
|
||
|
CryptoDevBackendSessionInfo *sess_info,
|
||
|
uint32_t queue_index,
|
||
|
CryptoDevCompletionFunc cb,
|
||
|
void *opaque)
|
||
|
{
|
||
|
CryptoDevBackendAsymSessionInfo *asym_sess_info;
|
||
|
CryptoDevBackendLKCF *lkcf =
|
||
|
CRYPTODEV_BACKEND_LKCF(backend);
|
||
|
int ret;
|
||
|
|
||
|
switch (sess_info->op_code) {
|
||
|
case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
|
||
|
asym_sess_info = &sess_info->u.asym_sess_info;
|
||
|
ret = cryptodev_lkcf_create_asym_session(
|
||
|
lkcf, asym_sess_info, &sess_info->session_id);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ret = -VIRTIO_CRYPTO_NOTSUPP;
|
||
|
error_report("Unsupported opcode: %" PRIu32 "",
|
||
|
sess_info->op_code);
|
||
|
break;
|
||
|
}
|
||
|
if (cb) {
|
||
|
cb(opaque, ret);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
|
||
|
uint64_t session_id,
|
||
|
uint32_t queue_index,
|
||
|
CryptoDevCompletionFunc cb,
|
||
|
void *opaque)
|
||
|
{
|
||
|
CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
|
||
|
CryptoDevBackendLKCFSession *session;
|
||
|
|
||
|
assert(session_id < MAX_SESSIONS && lkcf->sess[session_id]);
|
||
|
session = lkcf->sess[session_id];
|
||
|
lkcf->sess[session_id] = NULL;
|
||
|
|
||
|
g_free(session->key);
|
||
|
g_free(session);
|
||
|
|
||
|
if (cb) {
|
||
|
cb(opaque, VIRTIO_CRYPTO_OK);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data)
|
||
|
{
|
||
|
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
|
||
|
|
||
|
bc->init = cryptodev_lkcf_init;
|
||
|
bc->cleanup = cryptodev_lkcf_cleanup;
|
||
|
bc->create_session = cryptodev_lkcf_create_session;
|
||
|
bc->close_session = cryptodev_lkcf_close_session;
|
||
|
bc->do_op = cryptodev_lkcf_operation;
|
||
|
}
|
||
|
|
||
|
static const TypeInfo cryptodev_builtin_info = {
|
||
|
.name = TYPE_CRYPTODEV_BACKEND_LKCF,
|
||
|
.parent = TYPE_CRYPTODEV_BACKEND,
|
||
|
.class_init = cryptodev_lkcf_class_init,
|
||
|
.instance_size = sizeof(CryptoDevBackendLKCF),
|
||
|
};
|
||
|
|
||
|
static void cryptodev_lkcf_register_types(void)
|
||
|
{
|
||
|
type_register_static(&cryptodev_builtin_info);
|
||
|
}
|
||
|
|
||
|
type_init(cryptodev_lkcf_register_types);
|