aboutsummaryrefslogtreecommitdiff
path: root/backends/cryptodev-builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/cryptodev-builtin.c')
-rw-r--r--backends/cryptodev-builtin.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
new file mode 100644
index 0000000000..eda954b2a2
--- /dev/null
+++ b/backends/cryptodev-builtin.c
@@ -0,0 +1,361 @@
+/*
+ * QEMU Cryptodev backend for QEMU cipher APIs
+ *
+ * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
+ *
+ * Authors:
+ * Gonglei <arei.gonglei@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_CRYPTODEV_BACKEND_BUILTIN:
+ * name of backend that uses QEMU cipher API
+ */
+#define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin"
+
+#define CRYPTODEV_BACKEND_BUILTIN(obj) \
+ OBJECT_CHECK(CryptoDevBackendBuiltin, \
+ (obj), TYPE_CRYPTODEV_BACKEND_BUILTIN)
+
+typedef struct CryptoDevBackendBuiltin
+ CryptoDevBackendBuiltin;
+
+typedef struct CryptoDevBackendBuiltinSession {
+ QCryptoCipher *cipher;
+ uint8_t direction; /* encryption or decryption */
+ uint8_t type; /* cipher? hash? aead? */
+ QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
+} CryptoDevBackendBuiltinSession;
+
+/* Max number of symmetric sessions */
+#define MAX_NUM_SESSIONS 256
+
+#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
+#define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN 64
+
+struct CryptoDevBackendBuiltin {
+ CryptoDevBackend parent_obj;
+
+ CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS];
+};
+
+static void cryptodev_builtin_init(
+ CryptoDevBackend *backend, Error **errp)
+{
+ /* Only support one queue */
+ int queues = backend->conf.peers.queues;
+ CryptoDevBackendClient *cc;
+
+ if (queues != 1) {
+ error_setg(errp,
+ "Only support one queue in cryptdov-builtin backend");
+ return;
+ }
+
+ cc = 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;
+ /*
+ * Set the Maximum length of crypto request.
+ * Why this value? Just avoid to overflow when
+ * memory allocation for each crypto request.
+ */
+ backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+ backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
+ backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
+}
+
+static int
+cryptodev_builtin_get_unused_session_index(
+ CryptoDevBackendBuiltin *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(
+ CryptoDevBackendBuiltin *builtin,
+ CryptoDevBackendSymSessionInfo *sess_info,
+ Error **errp)
+{
+ int algo;
+ int mode;
+ QCryptoCipher *cipher;
+ int index;
+ CryptoDevBackendBuiltinSession *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(CryptoDevBackendBuiltinSession, 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(
+ CryptoDevBackend *backend,
+ CryptoDevBackendSymSessionInfo *sess_info,
+ uint32_t queue_index, Error **errp)
+{
+ CryptoDevBackendBuiltin *builtin =
+ 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(
+ CryptoDevBackend *backend,
+ uint64_t session_id,
+ uint32_t queue_index, Error **errp)
+{
+ CryptoDevBackendBuiltin *builtin =
+ 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(
+ CryptoDevBackend *backend,
+ CryptoDevBackendSymOpInfo *op_info,
+ uint32_t queue_index, Error **errp)
+{
+ CryptoDevBackendBuiltin *builtin =
+ CRYPTODEV_BACKEND_BUILTIN(backend);
+ CryptoDevBackendBuiltinSession *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;
+ }
+
+ if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
+ error_setg(errp,
+ "Algorithm chain is unsupported for cryptdoev-builtin");
+ return -VIRTIO_CRYPTO_NOTSUPP;
+ }
+
+ 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 VIRTIO_CRYPTO_OK;
+}
+
+static void cryptodev_builtin_cleanup(
+ CryptoDevBackend *backend,
+ Error **errp)
+{
+ CryptoDevBackendBuiltin *builtin =
+ CRYPTODEV_BACKEND_BUILTIN(backend);
+ size_t i;
+ int queues = backend->conf.peers.queues;
+ CryptoDevBackendClient *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) {
+ cryptodev_backend_free_client(cc);
+ backend->conf.peers.ccs[i] = NULL;
+ }
+ }
+}
+
+static void
+cryptodev_builtin_class_init(ObjectClass *oc, void *data)
+{
+ CryptoDevBackendClass *bc = 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_CRYPTODEV_BACKEND_BUILTIN,
+ .parent = TYPE_CRYPTODEV_BACKEND,
+ .class_init = cryptodev_builtin_class_init,
+ .instance_size = sizeof(CryptoDevBackendBuiltin),
+};
+
+static void
+cryptodev_builtin_register_types(void)
+{
+ type_register_static(&cryptodev_builtin_info);
+}
+
+type_init(cryptodev_builtin_register_types);