qemu/backends/cryptodev-builtin.c
<<
>>
Prefs
   1/*
   2 * QEMU Cryptodev backend for QEMU cipher APIs
   3 *
   4 * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
   5 *
   6 * Authors:
   7 *    Gonglei <arei.gonglei@huawei.com>
   8 *
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation; either
  12 * version 2 of the License, or (at your option) any later version.
  13 *
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  21 *
  22 */
  23
  24#include "qemu/osdep.h"
  25#include "sysemu/cryptodev.h"
  26#include "qapi/error.h"
  27#include "standard-headers/linux/virtio_crypto.h"
  28#include "crypto/cipher.h"
  29
  30
  31/**
  32 * @TYPE_CRYPTODEV_BACKEND_BUILTIN:
  33 * name of backend that uses QEMU cipher API
  34 */
  35#define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin"
  36
  37#define CRYPTODEV_BACKEND_BUILTIN(obj) \
  38    OBJECT_CHECK(CryptoDevBackendBuiltin, \
  39                 (obj), TYPE_CRYPTODEV_BACKEND_BUILTIN)
  40
  41typedef struct CryptoDevBackendBuiltin
  42                         CryptoDevBackendBuiltin;
  43
  44typedef struct CryptoDevBackendBuiltinSession {
  45    QCryptoCipher *cipher;
  46    uint8_t direction; /* encryption or decryption */
  47    uint8_t type; /* cipher? hash? aead? */
  48    QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
  49} CryptoDevBackendBuiltinSession;
  50
  51/* Max number of symmetric sessions */
  52#define MAX_NUM_SESSIONS 256
  53
  54#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN    512
  55#define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN  64
  56
  57struct CryptoDevBackendBuiltin {
  58    CryptoDevBackend parent_obj;
  59
  60    CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS];
  61};
  62
  63static void cryptodev_builtin_init(
  64             CryptoDevBackend *backend, Error **errp)
  65{
  66    /* Only support one queue */
  67    int queues = backend->conf.peers.queues;
  68    CryptoDevBackendClient *cc;
  69
  70    if (queues != 1) {
  71        error_setg(errp,
  72                  "Only support one queue in cryptdov-builtin backend");
  73        return;
  74    }
  75
  76    cc = cryptodev_backend_new_client(
  77              "cryptodev-builtin", NULL);
  78    cc->info_str = g_strdup_printf("cryptodev-builtin0");
  79    cc->queue_index = 0;
  80    cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN;
  81    backend->conf.peers.ccs[0] = cc;
  82
  83    backend->conf.crypto_services =
  84                         1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
  85                         1u << VIRTIO_CRYPTO_SERVICE_HASH |
  86                         1u << VIRTIO_CRYPTO_SERVICE_MAC;
  87    backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
  88    backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
  89    /*
  90     * Set the Maximum length of crypto request.
  91     * Why this value? Just avoid to overflow when
  92     * memory allocation for each crypto request.
  93     */
  94    backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
  95    backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
  96    backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
  97
  98    cryptodev_backend_set_ready(backend, true);
  99}
 100
 101static int
 102cryptodev_builtin_get_unused_session_index(
 103                 CryptoDevBackendBuiltin *builtin)
 104{
 105    size_t i;
 106
 107    for (i = 0; i < MAX_NUM_SESSIONS; i++) {
 108        if (builtin->sessions[i] == NULL) {
 109            return i;
 110        }
 111    }
 112
 113    return -1;
 114}
 115
 116#define AES_KEYSIZE_128 16
 117#define AES_KEYSIZE_192 24
 118#define AES_KEYSIZE_256 32
 119#define AES_KEYSIZE_128_XTS AES_KEYSIZE_256
 120#define AES_KEYSIZE_256_XTS 64
 121
 122static int
 123cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp)
 124{
 125    int algo;
 126
 127    if (key_len == AES_KEYSIZE_128) {
 128        algo = QCRYPTO_CIPHER_ALG_AES_128;
 129    } else if (key_len == AES_KEYSIZE_192) {
 130        algo = QCRYPTO_CIPHER_ALG_AES_192;
 131    } else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */
 132        if (mode == QCRYPTO_CIPHER_MODE_XTS) {
 133            algo = QCRYPTO_CIPHER_ALG_AES_128;
 134        } else {
 135            algo = QCRYPTO_CIPHER_ALG_AES_256;
 136        }
 137    } else if (key_len == AES_KEYSIZE_256_XTS) {
 138        if (mode == QCRYPTO_CIPHER_MODE_XTS) {
 139            algo = QCRYPTO_CIPHER_ALG_AES_256;
 140        } else {
 141            goto err;
 142        }
 143    } else {
 144        goto err;
 145    }
 146
 147    return algo;
 148
 149err:
 150   error_setg(errp, "Unsupported key length :%u", key_len);
 151   return -1;
 152}
 153
 154static int cryptodev_builtin_create_cipher_session(
 155                    CryptoDevBackendBuiltin *builtin,
 156                    CryptoDevBackendSymSessionInfo *sess_info,
 157                    Error **errp)
 158{
 159    int algo;
 160    int mode;
 161    QCryptoCipher *cipher;
 162    int index;
 163    CryptoDevBackendBuiltinSession *sess;
 164
 165    if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) {
 166        error_setg(errp, "Unsupported optype :%u", sess_info->op_type);
 167        return -1;
 168    }
 169
 170    index = cryptodev_builtin_get_unused_session_index(builtin);
 171    if (index < 0) {
 172        error_setg(errp, "Total number of sessions created exceeds %u",
 173                  MAX_NUM_SESSIONS);
 174        return -1;
 175    }
 176
 177    switch (sess_info->cipher_alg) {
 178    case VIRTIO_CRYPTO_CIPHER_AES_ECB:
 179        mode = QCRYPTO_CIPHER_MODE_ECB;
 180        algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
 181                                                    mode, errp);
 182        if (algo < 0)  {
 183            return -1;
 184        }
 185        break;
 186    case VIRTIO_CRYPTO_CIPHER_AES_CBC:
 187        mode = QCRYPTO_CIPHER_MODE_CBC;
 188        algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
 189                                                    mode, errp);
 190        if (algo < 0)  {
 191            return -1;
 192        }
 193        break;
 194    case VIRTIO_CRYPTO_CIPHER_AES_CTR:
 195        mode = QCRYPTO_CIPHER_MODE_CTR;
 196        algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
 197                                                    mode, errp);
 198        if (algo < 0)  {
 199            return -1;
 200        }
 201        break;
 202    case VIRTIO_CRYPTO_CIPHER_AES_XTS:
 203        mode = QCRYPTO_CIPHER_MODE_XTS;
 204        algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
 205                                                    mode, errp);
 206        if (algo < 0)  {
 207            return -1;
 208        }
 209        break;
 210    case VIRTIO_CRYPTO_CIPHER_3DES_ECB:
 211        mode = QCRYPTO_CIPHER_MODE_ECB;
 212        algo = QCRYPTO_CIPHER_ALG_3DES;
 213        break;
 214    case VIRTIO_CRYPTO_CIPHER_3DES_CBC:
 215        mode = QCRYPTO_CIPHER_MODE_CBC;
 216        algo = QCRYPTO_CIPHER_ALG_3DES;
 217        break;
 218    case VIRTIO_CRYPTO_CIPHER_3DES_CTR:
 219        mode = QCRYPTO_CIPHER_MODE_CTR;
 220        algo = QCRYPTO_CIPHER_ALG_3DES;
 221        break;
 222    default:
 223        error_setg(errp, "Unsupported cipher alg :%u",
 224                   sess_info->cipher_alg);
 225        return -1;
 226    }
 227
 228    cipher = qcrypto_cipher_new(algo, mode,
 229                               sess_info->cipher_key,
 230                               sess_info->key_len,
 231                               errp);
 232    if (!cipher) {
 233        return -1;
 234    }
 235
 236    sess = g_new0(CryptoDevBackendBuiltinSession, 1);
 237    sess->cipher = cipher;
 238    sess->direction = sess_info->direction;
 239    sess->type = sess_info->op_type;
 240
 241    builtin->sessions[index] = sess;
 242
 243    return index;
 244}
 245
 246static int64_t cryptodev_builtin_sym_create_session(
 247           CryptoDevBackend *backend,
 248           CryptoDevBackendSymSessionInfo *sess_info,
 249           uint32_t queue_index, Error **errp)
 250{
 251    CryptoDevBackendBuiltin *builtin =
 252                      CRYPTODEV_BACKEND_BUILTIN(backend);
 253    int64_t session_id = -1;
 254    int ret;
 255
 256    switch (sess_info->op_code) {
 257    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
 258        ret = cryptodev_builtin_create_cipher_session(
 259                           builtin, sess_info, errp);
 260        if (ret < 0) {
 261            return ret;
 262        } else {
 263            session_id = ret;
 264        }
 265        break;
 266    case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
 267    case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
 268    default:
 269        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
 270                   sess_info->op_code);
 271        return -1;
 272    }
 273
 274    return session_id;
 275}
 276
 277static int cryptodev_builtin_sym_close_session(
 278           CryptoDevBackend *backend,
 279           uint64_t session_id,
 280           uint32_t queue_index, Error **errp)
 281{
 282    CryptoDevBackendBuiltin *builtin =
 283                      CRYPTODEV_BACKEND_BUILTIN(backend);
 284
 285    if (session_id >= MAX_NUM_SESSIONS ||
 286              builtin->sessions[session_id] == NULL) {
 287        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
 288                      session_id);
 289        return -1;
 290    }
 291
 292    qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
 293    g_free(builtin->sessions[session_id]);
 294    builtin->sessions[session_id] = NULL;
 295    return 0;
 296}
 297
 298static int cryptodev_builtin_sym_operation(
 299                 CryptoDevBackend *backend,
 300                 CryptoDevBackendSymOpInfo *op_info,
 301                 uint32_t queue_index, Error **errp)
 302{
 303    CryptoDevBackendBuiltin *builtin =
 304                      CRYPTODEV_BACKEND_BUILTIN(backend);
 305    CryptoDevBackendBuiltinSession *sess;
 306    int ret;
 307
 308    if (op_info->session_id >= MAX_NUM_SESSIONS ||
 309              builtin->sessions[op_info->session_id] == NULL) {
 310        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
 311                   op_info->session_id);
 312        return -VIRTIO_CRYPTO_INVSESS;
 313    }
 314
 315    if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
 316        error_setg(errp,
 317               "Algorithm chain is unsupported for cryptdoev-builtin");
 318        return -VIRTIO_CRYPTO_NOTSUPP;
 319    }
 320
 321    sess = builtin->sessions[op_info->session_id];
 322
 323    if (op_info->iv_len > 0) {
 324        ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
 325                                   op_info->iv_len, errp);
 326        if (ret < 0) {
 327            return -VIRTIO_CRYPTO_ERR;
 328        }
 329    }
 330
 331    if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) {
 332        ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src,
 333                                     op_info->dst, op_info->src_len, errp);
 334        if (ret < 0) {
 335            return -VIRTIO_CRYPTO_ERR;
 336        }
 337    } else {
 338        ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src,
 339                                     op_info->dst, op_info->src_len, errp);
 340        if (ret < 0) {
 341            return -VIRTIO_CRYPTO_ERR;
 342        }
 343    }
 344    return VIRTIO_CRYPTO_OK;
 345}
 346
 347static void cryptodev_builtin_cleanup(
 348             CryptoDevBackend *backend,
 349             Error **errp)
 350{
 351    CryptoDevBackendBuiltin *builtin =
 352                      CRYPTODEV_BACKEND_BUILTIN(backend);
 353    size_t i;
 354    int queues = backend->conf.peers.queues;
 355    CryptoDevBackendClient *cc;
 356
 357    for (i = 0; i < MAX_NUM_SESSIONS; i++) {
 358        if (builtin->sessions[i] != NULL) {
 359            cryptodev_builtin_sym_close_session(
 360                    backend, i, 0, errp);
 361        }
 362    }
 363
 364    for (i = 0; i < queues; i++) {
 365        cc = backend->conf.peers.ccs[i];
 366        if (cc) {
 367            cryptodev_backend_free_client(cc);
 368            backend->conf.peers.ccs[i] = NULL;
 369        }
 370    }
 371
 372    cryptodev_backend_set_ready(backend, false);
 373}
 374
 375static void
 376cryptodev_builtin_class_init(ObjectClass *oc, void *data)
 377{
 378    CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
 379
 380    bc->init = cryptodev_builtin_init;
 381    bc->cleanup = cryptodev_builtin_cleanup;
 382    bc->create_session = cryptodev_builtin_sym_create_session;
 383    bc->close_session = cryptodev_builtin_sym_close_session;
 384    bc->do_sym_op = cryptodev_builtin_sym_operation;
 385}
 386
 387static const TypeInfo cryptodev_builtin_info = {
 388    .name = TYPE_CRYPTODEV_BACKEND_BUILTIN,
 389    .parent = TYPE_CRYPTODEV_BACKEND,
 390    .class_init = cryptodev_builtin_class_init,
 391    .instance_size = sizeof(CryptoDevBackendBuiltin),
 392};
 393
 394static void
 395cryptodev_builtin_register_types(void)
 396{
 397    type_register_static(&cryptodev_builtin_info);
 398}
 399
 400type_init(cryptodev_builtin_register_types);
 401