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