The new cryptodev backend named cryptodev-builtin, which realized by QEMU cipher APIS. These APIs can be backed by either nettle or gcrypt.
Signed-off-by: Gonglei <arei.gong...@huawei.com> --- backends/Makefile.objs | 1 + backends/cryptodev-builtin.c | 345 +++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 18 +++ 3 files changed, 364 insertions(+) create mode 100644 backends/cryptodev-builtin.c diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 55bd43d..1846998 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -11,3 +11,4 @@ common-obj-y += hostmem.o hostmem-ram.o common-obj-$(CONFIG_LINUX) += hostmem-file.o common-obj-y += cryptodev.o +common-obj-y += cryptodev-builtin.o diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c new file mode 100644 index 0000000..1592208 --- /dev/null +++ b/backends/cryptodev-builtin.c @@ -0,0 +1,345 @@ +/* + * QEMU Cryptodev backend for QEMU cipher APIs + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Gonglei <arei.gong...@huawei.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 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 "sysemu/cryptodev.h" +#include "hw/boards.h" +#include "qapi/error.h" +#include "standard-headers/linux/virtio_crypto.h" +#include "crypto/cipher.h" + + +/** + * @TYPE_QCRYPTO_CRYPTODEV_BACKEND_BUILTIN: + * name of backend that uses QEMU cipher API + */ +#define TYPE_QCRYPTO_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin" + +#define QCRYPTO_CRYPTODEV_BACKEND_BUILTIN(obj) \ + OBJECT_CHECK(QCryptoCryptoDevBackendBuiltin, \ + (obj), TYPE_QCRYPTO_CRYPTODEV_BACKEND_BUILTIN) + +typedef struct QCryptoCryptoDevBackendBuiltin + QCryptoCryptoDevBackendBuiltin; + +typedef struct QCryptoCryptoDevBackendBuiltinSession { + QCryptoCipher *cipher; + uint8_t direction; /* encryption or decryption */ + uint8_t type; /* cipher? hash? aead? */ + QTAILQ_ENTRY(QCryptoCryptoDevBackendBuiltinSession) next; +} QCryptoCryptoDevBackendBuiltinSession; + +/* Max number of symmetric sessions */ +#define MAX_NUM_SESSIONS 256 + + +struct QCryptoCryptoDevBackendBuiltin { + QCryptoCryptoDevBackend parent_obj; + + QCryptoCryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; +}; + +static void cryptodev_builtin_init( + QCryptoCryptoDevBackend *backend, Error **errp) +{ + /* Only support one queue */ + int queues = backend->conf.peers.queues; + QCryptoCryptoDevBackendClientState *cc; + + if (queues != 1) { + error_setg(errp, + "Only support one queue in cryptdov-builtin backend"); + return; + } + + cc = qcrypto_cryptodev_backend_new_client( + "cryptodev-builtin", NULL); + cc->info_str = g_strdup_printf("cryptodev-builtin0"); + cc->queue_index = 0; + backend->conf.peers.ccs[0] = cc; + + backend->conf.crypto_services = + 1u << VIRTIO_CRYPTO_SERVICE_CIPHER | + 1u << VIRTIO_CRYPTO_SERVICE_HASH | + 1u << VIRTIO_CRYPTO_SERVICE_MAC; + backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; + backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; +} + +static int +cryptodev_builtin_get_unused_session_index( + QCryptoCryptoDevBackendBuiltin *builtin) +{ + size_t i; + + for (i = 0; i < MAX_NUM_SESSIONS; i++) { + if (builtin->sessions[i] == NULL) { + return i; + } + } + + return -1; +} + +static int +cryptodev_builtin_get_aes_algo(uint32_t key_len, Error **errp) +{ + int algo; + + if (key_len == 128 / 8) { + algo = QCRYPTO_CIPHER_ALG_AES_128; + } else if (key_len == 192 / 8) { + algo = QCRYPTO_CIPHER_ALG_AES_192; + } else if (key_len == 256 / 8) { + algo = QCRYPTO_CIPHER_ALG_AES_256; + } else { + error_setg(errp, "Unsupported key length :%u", key_len); + return -1; + } + + return algo; +} + +static int cryptodev_builtin_create_cipher_session( + QCryptoCryptoDevBackendBuiltin *builtin, + QCryptoCryptoDevBackendSymSessionInfo *sess_info, + Error **errp) +{ + int algo; + int mode; + QCryptoCipher *cipher; + int index; + QCryptoCryptoDevBackendBuiltinSession *sess; + + if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) { + error_setg(errp, "Unsupported optype :%u", sess_info->op_type); + return -1; + } + + index = cryptodev_builtin_get_unused_session_index(builtin); + if (index < 0) { + error_setg(errp, "Total number of sessions created exceeds %u", + MAX_NUM_SESSIONS); + return -1; + } + + switch (sess_info->cipher_alg) { + case VIRTIO_CRYPTO_CIPHER_AES_ECB: + algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, + errp); + if (algo < 0) { + return -1; + } + mode = QCRYPTO_CIPHER_MODE_ECB; + break; + case VIRTIO_CRYPTO_CIPHER_AES_CBC: + algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, + errp); + if (algo < 0) { + return -1; + } + mode = QCRYPTO_CIPHER_MODE_CBC; + break; + case VIRTIO_CRYPTO_CIPHER_AES_CTR: + algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, + errp); + if (algo < 0) { + return -1; + } + mode = QCRYPTO_CIPHER_MODE_CTR; + break; + case VIRTIO_CRYPTO_CIPHER_DES_ECB: + algo = QCRYPTO_CIPHER_ALG_DES_RFB; + mode = QCRYPTO_CIPHER_MODE_ECB; + break; + default: + error_setg(errp, "Unsupported cipher alg :%u", + sess_info->cipher_alg); + return -1; + } + + cipher = qcrypto_cipher_new(algo, mode, + sess_info->cipher_key, + sess_info->key_len, + errp); + if (!cipher) { + return -1; + } + + sess = g_new0(QCryptoCryptoDevBackendBuiltinSession, 1); + sess->cipher = cipher; + sess->direction = sess_info->direction; + sess->type = sess_info->op_type; + + builtin->sessions[index] = sess; + + return index; +} + +static int64_t cryptodev_builtin_sym_create_session( + QCryptoCryptoDevBackend *backend, + QCryptoCryptoDevBackendSymSessionInfo *sess_info, + uint32_t queue_index, Error **errp) +{ + QCryptoCryptoDevBackendBuiltin *builtin = + QCRYPTO_CRYPTODEV_BACKEND_BUILTIN(backend); + int64_t session_id = -1; + int ret; + + switch (sess_info->op_code) { + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: + ret = cryptodev_builtin_create_cipher_session( + builtin, sess_info, errp); + if (ret < 0) { + return ret; + } else { + session_id = ret; + } + break; + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: + case VIRTIO_CRYPTO_MAC_CREATE_SESSION: + default: + error_setg(errp, "Unsupported opcode :%" PRIu32 "", + sess_info->op_code); + return -1; + } + + return session_id; +} + +static int cryptodev_builtin_sym_close_session( + QCryptoCryptoDevBackend *backend, + uint64_t session_id, + uint32_t queue_index, Error **errp) +{ + QCryptoCryptoDevBackendBuiltin *builtin = + QCRYPTO_CRYPTODEV_BACKEND_BUILTIN(backend); + + if (session_id >= MAX_NUM_SESSIONS || + builtin->sessions[session_id] == NULL) { + error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", + session_id); + return -1; + } + + qcrypto_cipher_free(builtin->sessions[session_id]->cipher); + g_free(builtin->sessions[session_id]); + builtin->sessions[session_id] = NULL; + return 0; +} + +static int cryptodev_builtin_sym_operation( + QCryptoCryptoDevBackend *backend, + QCryptoCryptoDevBackendSymOpInfo *op_info, + uint32_t queue_index, Error **errp) +{ + QCryptoCryptoDevBackendBuiltin *builtin = + QCRYPTO_CRYPTODEV_BACKEND_BUILTIN(backend); + QCryptoCryptoDevBackendBuiltinSession *sess; + int ret; + + if (op_info->session_id >= MAX_NUM_SESSIONS || + builtin->sessions[op_info->session_id] == NULL) { + error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); + return -VIRTIO_CRYPTO_INVSESS; + } + + sess = builtin->sessions[op_info->session_id]; + + ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, + op_info->iv_len, errp); + if (ret < 0) { + return -VIRTIO_CRYPTO_ERR; + } + + if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) { + ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src, + op_info->dst, op_info->src_len, errp); + if (ret < 0) { + return -VIRTIO_CRYPTO_ERR; + } + } else { + ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src, + op_info->dst, op_info->src_len, errp); + if (ret < 0) { + return -VIRTIO_CRYPTO_ERR; + } + } + return 0; +} + +static void cryptodev_builtin_cleanup( + QCryptoCryptoDevBackend *backend, + Error **errp) +{ + QCryptoCryptoDevBackendBuiltin *builtin = + QCRYPTO_CRYPTODEV_BACKEND_BUILTIN(backend); + size_t i; + int queues = backend->conf.peers.queues; + QCryptoCryptoDevBackendClientState *cc; + + for (i = 0; i < MAX_NUM_SESSIONS; i++) { + if (builtin->sessions[i] != NULL) { + cryptodev_builtin_sym_close_session( + backend, i, 0, errp); + } + } + + assert(queues == 1); + + for (i = 0; i < queues; i++) { + cc = backend->conf.peers.ccs[i]; + if (cc) { + qcrypto_cryptodev_backend_free_client(cc); + backend->conf.peers.ccs[i] = NULL; + } + } +} + +static void +cryptodev_builtin_class_init(ObjectClass *oc, void *data) +{ + QCryptoCryptoDevBackendClass *bc = QCRYPTO_CRYPTODEV_BACKEND_CLASS(oc); + + bc->init = cryptodev_builtin_init; + bc->cleanup = cryptodev_builtin_cleanup; + bc->create_session = cryptodev_builtin_sym_create_session; + bc->close_session = cryptodev_builtin_sym_close_session; + bc->do_sym_op = cryptodev_builtin_sym_operation; +} + +static const TypeInfo cryptodev_builtin_info = { + .name = TYPE_QCRYPTO_CRYPTODEV_BACKEND_BUILTIN, + .parent = TYPE_QCRYPTO_CRYPTODEV_BACKEND, + .class_init = cryptodev_builtin_class_init, + .instance_size = sizeof(QCryptoCryptoDevBackendBuiltin), +}; + +static void +cryptodev_builtin_register_types(void) +{ + type_register_static(&cryptodev_builtin_info); +} + +type_init(cryptodev_builtin_register_types); diff --git a/qemu-options.hx b/qemu-options.hx index 01f01df..616cd30 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3948,6 +3948,24 @@ secondary: If you want to know the detail of above command line, you can read the colo-compare git log. +@item -object cryptodev-backend-builtin,id=@var{id}[,queues=@var{queues}] + +Creates a cryptodev backend which executes crypto opreation from +the QEMU cipher APIS. The @var{id} parameter is +a unique ID that will be used to reference this cryptodev backend from +the @option{virtio-crypto} device. The @var{queues} parameter is optional, +which specify the queue number of cryptodev backend, the default of +@var{queues} is 1. + +@example + + # qemu-system-x86_64 \ + [...] \ + -object cryptodev-backend-builtin,id=cryptodev0 \ + -device virtio-crypto-pci,id=crypto0,cryptodev=cryptodev0 \ + [...] +@end example + @item -object secret,id=@var{id},data=@var{string},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] @item -object secret,id=@var{id},file=@var{filename},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] -- 1.7.12.4