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 | |
41 | typedef struct CryptoDevBackendBuiltin |
42 | CryptoDevBackendBuiltin; |
43 | |
44 | typedef 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 | |
57 | struct CryptoDevBackendBuiltin { |
58 | CryptoDevBackend parent_obj; |
59 | |
60 | CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; |
61 | }; |
62 | |
63 | static 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 | |
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 | 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 | |
347 | static 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 | |
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 | |