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 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 101 static int 102 cryptodev_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 122 static int 123 cryptodev_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 149 err: 150 error_setg(errp, "Unsupported key length :%u", key_len); 151 return -1; 152 } 153 154 static 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 246 static 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 277 static 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 298 static 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 ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, 324 op_info->iv_len, errp); 325 if (ret < 0) { 326 return -VIRTIO_CRYPTO_ERR; 327 } 328 329 if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) { 330 ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src, 331 op_info->dst, op_info->src_len, errp); 332 if (ret < 0) { 333 return -VIRTIO_CRYPTO_ERR; 334 } 335 } else { 336 ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src, 337 op_info->dst, op_info->src_len, errp); 338 if (ret < 0) { 339 return -VIRTIO_CRYPTO_ERR; 340 } 341 } 342 return VIRTIO_CRYPTO_OK; 343 } 344 345 static void cryptodev_builtin_cleanup( 346 CryptoDevBackend *backend, 347 Error **errp) 348 { 349 CryptoDevBackendBuiltin *builtin = 350 CRYPTODEV_BACKEND_BUILTIN(backend); 351 size_t i; 352 int queues = backend->conf.peers.queues; 353 CryptoDevBackendClient *cc; 354 355 for (i = 0; i < MAX_NUM_SESSIONS; i++) { 356 if (builtin->sessions[i] != NULL) { 357 cryptodev_builtin_sym_close_session( 358 backend, i, 0, errp); 359 } 360 } 361 362 assert(queues == 1); 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 375 static void 376 cryptodev_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 387 static 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 394 static void 395 cryptodev_builtin_register_types(void) 396 { 397 type_register_static(&cryptodev_builtin_info); 398 } 399 400 type_init(cryptodev_builtin_register_types); 401