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 99 static int 100 cryptodev_builtin_get_unused_session_index( 101 CryptoDevBackendBuiltin *builtin) 102 { 103 size_t i; 104 105 for (i = 0; i < MAX_NUM_SESSIONS; i++) { 106 if (builtin->sessions[i] == NULL) { 107 return i; 108 } 109 } 110 111 return -1; 112 } 113 114 static int 115 cryptodev_builtin_get_aes_algo(uint32_t key_len, Error **errp) 116 { 117 int algo; 118 119 if (key_len == 128 / 8) { 120 algo = QCRYPTO_CIPHER_ALG_AES_128; 121 } else if (key_len == 192 / 8) { 122 algo = QCRYPTO_CIPHER_ALG_AES_192; 123 } else if (key_len == 256 / 8) { 124 algo = QCRYPTO_CIPHER_ALG_AES_256; 125 } else { 126 error_setg(errp, "Unsupported key length :%u", key_len); 127 return -1; 128 } 129 130 return algo; 131 } 132 133 static int cryptodev_builtin_create_cipher_session( 134 CryptoDevBackendBuiltin *builtin, 135 CryptoDevBackendSymSessionInfo *sess_info, 136 Error **errp) 137 { 138 int algo; 139 int mode; 140 QCryptoCipher *cipher; 141 int index; 142 CryptoDevBackendBuiltinSession *sess; 143 144 if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) { 145 error_setg(errp, "Unsupported optype :%u", sess_info->op_type); 146 return -1; 147 } 148 149 index = cryptodev_builtin_get_unused_session_index(builtin); 150 if (index < 0) { 151 error_setg(errp, "Total number of sessions created exceeds %u", 152 MAX_NUM_SESSIONS); 153 return -1; 154 } 155 156 switch (sess_info->cipher_alg) { 157 case VIRTIO_CRYPTO_CIPHER_AES_ECB: 158 algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, 159 errp); 160 if (algo < 0) { 161 return -1; 162 } 163 mode = QCRYPTO_CIPHER_MODE_ECB; 164 break; 165 case VIRTIO_CRYPTO_CIPHER_AES_CBC: 166 algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, 167 errp); 168 if (algo < 0) { 169 return -1; 170 } 171 mode = QCRYPTO_CIPHER_MODE_CBC; 172 break; 173 case VIRTIO_CRYPTO_CIPHER_AES_CTR: 174 algo = cryptodev_builtin_get_aes_algo(sess_info->key_len, 175 errp); 176 if (algo < 0) { 177 return -1; 178 } 179 mode = QCRYPTO_CIPHER_MODE_CTR; 180 break; 181 case VIRTIO_CRYPTO_CIPHER_DES_ECB: 182 algo = QCRYPTO_CIPHER_ALG_DES_RFB; 183 mode = QCRYPTO_CIPHER_MODE_ECB; 184 break; 185 default: 186 error_setg(errp, "Unsupported cipher alg :%u", 187 sess_info->cipher_alg); 188 return -1; 189 } 190 191 cipher = qcrypto_cipher_new(algo, mode, 192 sess_info->cipher_key, 193 sess_info->key_len, 194 errp); 195 if (!cipher) { 196 return -1; 197 } 198 199 sess = g_new0(CryptoDevBackendBuiltinSession, 1); 200 sess->cipher = cipher; 201 sess->direction = sess_info->direction; 202 sess->type = sess_info->op_type; 203 204 builtin->sessions[index] = sess; 205 206 return index; 207 } 208 209 static int64_t cryptodev_builtin_sym_create_session( 210 CryptoDevBackend *backend, 211 CryptoDevBackendSymSessionInfo *sess_info, 212 uint32_t queue_index, Error **errp) 213 { 214 CryptoDevBackendBuiltin *builtin = 215 CRYPTODEV_BACKEND_BUILTIN(backend); 216 int64_t session_id = -1; 217 int ret; 218 219 switch (sess_info->op_code) { 220 case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: 221 ret = cryptodev_builtin_create_cipher_session( 222 builtin, sess_info, errp); 223 if (ret < 0) { 224 return ret; 225 } else { 226 session_id = ret; 227 } 228 break; 229 case VIRTIO_CRYPTO_HASH_CREATE_SESSION: 230 case VIRTIO_CRYPTO_MAC_CREATE_SESSION: 231 default: 232 error_setg(errp, "Unsupported opcode :%" PRIu32 "", 233 sess_info->op_code); 234 return -1; 235 } 236 237 return session_id; 238 } 239 240 static int cryptodev_builtin_sym_close_session( 241 CryptoDevBackend *backend, 242 uint64_t session_id, 243 uint32_t queue_index, Error **errp) 244 { 245 CryptoDevBackendBuiltin *builtin = 246 CRYPTODEV_BACKEND_BUILTIN(backend); 247 248 if (session_id >= MAX_NUM_SESSIONS || 249 builtin->sessions[session_id] == NULL) { 250 error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", 251 session_id); 252 return -1; 253 } 254 255 qcrypto_cipher_free(builtin->sessions[session_id]->cipher); 256 g_free(builtin->sessions[session_id]); 257 builtin->sessions[session_id] = NULL; 258 return 0; 259 } 260 261 static int cryptodev_builtin_sym_operation( 262 CryptoDevBackend *backend, 263 CryptoDevBackendSymOpInfo *op_info, 264 uint32_t queue_index, Error **errp) 265 { 266 CryptoDevBackendBuiltin *builtin = 267 CRYPTODEV_BACKEND_BUILTIN(backend); 268 CryptoDevBackendBuiltinSession *sess; 269 int ret; 270 271 if (op_info->session_id >= MAX_NUM_SESSIONS || 272 builtin->sessions[op_info->session_id] == NULL) { 273 error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", 274 op_info->session_id); 275 return -VIRTIO_CRYPTO_INVSESS; 276 } 277 278 if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { 279 error_setg(errp, 280 "Algorithm chain is unsupported for cryptdoev-builtin"); 281 return -VIRTIO_CRYPTO_NOTSUPP; 282 } 283 284 sess = builtin->sessions[op_info->session_id]; 285 286 ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, 287 op_info->iv_len, errp); 288 if (ret < 0) { 289 return -VIRTIO_CRYPTO_ERR; 290 } 291 292 if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) { 293 ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src, 294 op_info->dst, op_info->src_len, errp); 295 if (ret < 0) { 296 return -VIRTIO_CRYPTO_ERR; 297 } 298 } else { 299 ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src, 300 op_info->dst, op_info->src_len, errp); 301 if (ret < 0) { 302 return -VIRTIO_CRYPTO_ERR; 303 } 304 } 305 return VIRTIO_CRYPTO_OK; 306 } 307 308 static void cryptodev_builtin_cleanup( 309 CryptoDevBackend *backend, 310 Error **errp) 311 { 312 CryptoDevBackendBuiltin *builtin = 313 CRYPTODEV_BACKEND_BUILTIN(backend); 314 size_t i; 315 int queues = backend->conf.peers.queues; 316 CryptoDevBackendClient *cc; 317 318 for (i = 0; i < MAX_NUM_SESSIONS; i++) { 319 if (builtin->sessions[i] != NULL) { 320 cryptodev_builtin_sym_close_session( 321 backend, i, 0, errp); 322 } 323 } 324 325 assert(queues == 1); 326 327 for (i = 0; i < queues; i++) { 328 cc = backend->conf.peers.ccs[i]; 329 if (cc) { 330 cryptodev_backend_free_client(cc); 331 backend->conf.peers.ccs[i] = NULL; 332 } 333 } 334 } 335 336 static void 337 cryptodev_builtin_class_init(ObjectClass *oc, void *data) 338 { 339 CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); 340 341 bc->init = cryptodev_builtin_init; 342 bc->cleanup = cryptodev_builtin_cleanup; 343 bc->create_session = cryptodev_builtin_sym_create_session; 344 bc->close_session = cryptodev_builtin_sym_close_session; 345 bc->do_sym_op = cryptodev_builtin_sym_operation; 346 } 347 348 static const TypeInfo cryptodev_builtin_info = { 349 .name = TYPE_CRYPTODEV_BACKEND_BUILTIN, 350 .parent = TYPE_CRYPTODEV_BACKEND, 351 .class_init = cryptodev_builtin_class_init, 352 .instance_size = sizeof(CryptoDevBackendBuiltin), 353 }; 354 355 static void 356 cryptodev_builtin_register_types(void) 357 { 358 type_register_static(&cryptodev_builtin_info); 359 } 360 361 type_init(cryptodev_builtin_register_types); 362