2 * QEMU Cryptodev backend for QEMU cipher APIs
4 * Copyright (c) 2022 Bytedance.Inc
7 * lei he <helei.sig11@bytedance.com>
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.1 of the License, or (at your option) any later version.
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.
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/>.
24 #include "qemu/osdep.h"
25 #include "crypto/cipher.h"
26 #include "crypto/akcipher.h"
27 #include "qapi/error.h"
28 #include "qemu/main-loop.h"
29 #include "qemu/thread.h"
30 #include "qemu/error-report.h"
31 #include "qemu/queue.h"
32 #include "qom/object.h"
33 #include "sysemu/cryptodev.h"
34 #include "standard-headers/linux/virtio_crypto.h"
37 #include <sys/eventfd.h>
40 * @TYPE_CRYPTODEV_BACKEND_LKCF:
41 * name of backend that uses linux kernel crypto framework
43 #define TYPE_CRYPTODEV_BACKEND_LKCF "cryptodev-backend-lkcf"
45 OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendLKCF
, CRYPTODEV_BACKEND_LKCF
)
47 #define INVALID_KEY_ID -1
48 #define MAX_SESSIONS 256
49 #define NR_WORKER_THREAD 64
51 #define KCTL_KEY_TYPE_PKEY "asymmetric"
53 * Here the key is uploaded to the thread-keyring of worker thread, at least
55 * 1. process keyring seems to behave unexpectedly if main-thread does not
56 * create the keyring before creating any other thread.
57 * 2. at present, the guest kernel never perform multiple operations on a
59 * 3. it can reduce the load of the main-loop because the key passed by the
60 * guest kernel has been already checked.
62 #define KCTL_KEY_RING KEY_SPEC_THREAD_KEYRING
64 typedef struct CryptoDevBackendLKCFSession
{
67 QCryptoAkCipherKeyType keytype
;
68 QCryptoAkCipherOptions akcipher_opts
;
69 } CryptoDevBackendLKCFSession
;
71 typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF
;
72 typedef struct CryptoDevLKCFTask CryptoDevLKCFTask
;
73 struct CryptoDevLKCFTask
{
74 CryptoDevBackendLKCFSession
*sess
;
75 CryptoDevBackendOpInfo
*op_info
;
76 CryptoDevCompletionFunc cb
;
79 CryptoDevBackendLKCF
*lkcf
;
80 QSIMPLEQ_ENTRY(CryptoDevLKCFTask
) queue
;
83 typedef struct CryptoDevBackendLKCF
{
84 CryptoDevBackend parent_obj
;
85 CryptoDevBackendLKCFSession
*sess
[MAX_SESSIONS
];
86 QSIMPLEQ_HEAD(, CryptoDevLKCFTask
) requests
;
87 QSIMPLEQ_HEAD(, CryptoDevLKCFTask
) responses
;
93 * There is no async interface for asymmetric keys like AF_ALG sockets,
94 * we don't seem to have better way than create a lots of thread.
96 QemuThread worker_threads
[NR_WORKER_THREAD
];
99 } CryptoDevBackendLKCF
;
101 static void *cryptodev_lkcf_worker(void *arg
);
102 static int cryptodev_lkcf_close_session(CryptoDevBackend
*backend
,
104 uint32_t queue_index
,
105 CryptoDevCompletionFunc cb
,
108 static void cryptodev_lkcf_handle_response(void *opaque
)
110 CryptoDevBackendLKCF
*lkcf
= (CryptoDevBackendLKCF
*)opaque
;
111 QSIMPLEQ_HEAD(, CryptoDevLKCFTask
) responses
;
112 CryptoDevLKCFTask
*task
, *next
;
115 QSIMPLEQ_INIT(&responses
);
116 eventfd_read(lkcf
->eventfd
, &nevent
);
118 qemu_mutex_lock(&lkcf
->rsp_mutex
);
119 QSIMPLEQ_PREPEND(&responses
, &lkcf
->responses
);
120 qemu_mutex_unlock(&lkcf
->rsp_mutex
);
122 QSIMPLEQ_FOREACH_SAFE(task
, &responses
, queue
, next
) {
124 task
->cb(task
->opaque
, task
->status
);
130 static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions
*opts
,
135 QCryptoAkCipherOptionsRSA
*rsa_opt
;
136 if (opts
->alg
!= QCRYPTO_AKCIPHER_ALG_RSA
) {
137 error_setg(errp
, "Unsupported alg: %u", opts
->alg
);
141 rsa_opt
= &opts
->u
.rsa
;
142 if (rsa_opt
->padding_alg
== QCRYPTO_RSA_PADDING_ALG_PKCS1
) {
143 snprintf(key_desc
, desc_len
, "enc=%s hash=%s",
144 QCryptoRSAPaddingAlgorithm_str(rsa_opt
->padding_alg
),
145 QCryptoHashAlgorithm_str(rsa_opt
->hash_alg
));
148 snprintf(key_desc
, desc_len
, "enc=%s",
149 QCryptoRSAPaddingAlgorithm_str(rsa_opt
->padding_alg
));
154 static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg
,
156 QCryptoAkCipherOptionsRSA
*opt
,
159 if (virtio_padding_alg
== VIRTIO_CRYPTO_RSA_PKCS1_PADDING
) {
160 opt
->padding_alg
= QCRYPTO_RSA_PADDING_ALG_PKCS1
;
162 switch (virtio_hash_alg
) {
163 case VIRTIO_CRYPTO_RSA_MD5
:
164 opt
->hash_alg
= QCRYPTO_HASH_ALG_MD5
;
167 case VIRTIO_CRYPTO_RSA_SHA1
:
168 opt
->hash_alg
= QCRYPTO_HASH_ALG_SHA1
;
171 case VIRTIO_CRYPTO_RSA_SHA256
:
172 opt
->hash_alg
= QCRYPTO_HASH_ALG_SHA256
;
175 case VIRTIO_CRYPTO_RSA_SHA512
:
176 opt
->hash_alg
= QCRYPTO_HASH_ALG_SHA512
;
180 error_setg(errp
, "Unsupported rsa hash algo: %d", virtio_hash_alg
);
186 if (virtio_padding_alg
== VIRTIO_CRYPTO_RSA_RAW_PADDING
) {
187 opt
->padding_alg
= QCRYPTO_RSA_PADDING_ALG_RAW
;
191 error_setg(errp
, "Unsupported rsa padding algo: %u", virtio_padding_alg
);
195 static int cryptodev_lkcf_get_unused_session_index(CryptoDevBackendLKCF
*lkcf
)
199 for (i
= 0; i
< MAX_SESSIONS
; i
++) {
200 if (lkcf
->sess
[i
] == NULL
) {
207 static void cryptodev_lkcf_init(CryptoDevBackend
*backend
, Error
**errp
)
209 /* Only support one queue */
210 int queues
= backend
->conf
.peers
.queues
, i
;
211 CryptoDevBackendClient
*cc
;
212 CryptoDevBackendLKCF
*lkcf
=
213 CRYPTODEV_BACKEND_LKCF(backend
);
217 "Only support one queue in cryptodev-builtin backend");
220 lkcf
->eventfd
= eventfd(0, 0);
221 if (lkcf
->eventfd
< 0) {
222 error_setg(errp
, "Failed to create eventfd: %d", errno
);
226 cc
= cryptodev_backend_new_client();
227 cc
->info_str
= g_strdup_printf("cryptodev-lkcf0");
229 cc
->type
= QCRYPTODEV_BACKEND_TYPE_LKCF
;
230 backend
->conf
.peers
.ccs
[0] = cc
;
232 backend
->conf
.crypto_services
=
233 1u << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER
;
234 backend
->conf
.akcipher_algo
= 1u << VIRTIO_CRYPTO_AKCIPHER_RSA
;
235 lkcf
->running
= true;
237 QSIMPLEQ_INIT(&lkcf
->requests
);
238 QSIMPLEQ_INIT(&lkcf
->responses
);
239 qemu_mutex_init(&lkcf
->mutex
);
240 qemu_mutex_init(&lkcf
->rsp_mutex
);
241 qemu_cond_init(&lkcf
->cond
);
242 for (i
= 0; i
< NR_WORKER_THREAD
; i
++) {
243 qemu_thread_create(&lkcf
->worker_threads
[i
], "lkcf-worker",
244 cryptodev_lkcf_worker
, lkcf
, 0);
247 lkcf
->eventfd
, cryptodev_lkcf_handle_response
, NULL
, lkcf
);
248 cryptodev_backend_set_ready(backend
, true);
251 static void cryptodev_lkcf_cleanup(CryptoDevBackend
*backend
, Error
**errp
)
253 CryptoDevBackendLKCF
*lkcf
= CRYPTODEV_BACKEND_LKCF(backend
);
255 int queues
= backend
->conf
.peers
.queues
;
256 CryptoDevBackendClient
*cc
;
257 CryptoDevLKCFTask
*task
, *next
;
259 qemu_mutex_lock(&lkcf
->mutex
);
260 lkcf
->running
= false;
261 qemu_mutex_unlock(&lkcf
->mutex
);
262 qemu_cond_broadcast(&lkcf
->cond
);
264 close(lkcf
->eventfd
);
265 for (i
= 0; i
< NR_WORKER_THREAD
; i
++) {
266 qemu_thread_join(&lkcf
->worker_threads
[i
]);
269 QSIMPLEQ_FOREACH_SAFE(task
, &lkcf
->requests
, queue
, next
) {
271 task
->cb(task
->opaque
, task
->status
);
276 QSIMPLEQ_FOREACH_SAFE(task
, &lkcf
->responses
, queue
, next
) {
278 task
->cb(task
->opaque
, task
->status
);
283 qemu_mutex_destroy(&lkcf
->mutex
);
284 qemu_cond_destroy(&lkcf
->cond
);
285 qemu_mutex_destroy(&lkcf
->rsp_mutex
);
287 for (i
= 0; i
< MAX_SESSIONS
; i
++) {
288 if (lkcf
->sess
[i
] != NULL
) {
289 cryptodev_lkcf_close_session(backend
, i
, 0, NULL
, NULL
);
293 for (i
= 0; i
< queues
; i
++) {
294 cc
= backend
->conf
.peers
.ccs
[i
];
296 cryptodev_backend_free_client(cc
);
297 backend
->conf
.peers
.ccs
[i
] = NULL
;
301 cryptodev_backend_set_ready(backend
, false);
304 static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask
*task
)
306 CryptoDevBackendLKCFSession
*session
= task
->sess
;
307 CryptoDevBackendAsymOpInfo
*asym_op_info
;
309 int ret
, status
, op_code
= task
->op_info
->op_code
;
311 g_autofree
uint8_t *p8info
= NULL
;
312 Error
*local_error
= NULL
;
313 key_serial_t key_id
= INVALID_KEY_ID
;
315 g_autoptr(QCryptoAkCipher
) akcipher
= NULL
;
318 * We only offload private key session:
319 * 1. currently, the Linux kernel can only accept public key wrapped
320 * with X.509 certificates, but unfortunately the cost of making a
321 * ceritificate with public key is too expensive.
322 * 2. generally, public key related compution is fast, just compute it with
325 if (session
->keytype
== QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE
) {
326 if (qcrypto_akcipher_export_p8info(&session
->akcipher_opts
,
327 session
->key
, session
->keylen
,
328 &p8info
, &p8info_len
,
329 &local_error
) != 0 ||
330 cryptodev_lkcf_set_op_desc(&session
->akcipher_opts
, op_desc
,
331 sizeof(op_desc
), &local_error
) != 0) {
332 error_report_err(local_error
);
334 key_id
= add_key(KCTL_KEY_TYPE_PKEY
, "lkcf-backend-priv-key",
335 p8info
, p8info_len
, KCTL_KEY_RING
);
340 if (!qcrypto_akcipher_supports(&session
->akcipher_opts
)) {
341 status
= -VIRTIO_CRYPTO_NOTSUPP
;
344 akcipher
= qcrypto_akcipher_new(&session
->akcipher_opts
,
346 session
->key
, session
->keylen
,
349 status
= -VIRTIO_CRYPTO_ERR
;
354 asym_op_info
= task
->op_info
->u
.asym_op_info
;
356 case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT
:
358 ret
= keyctl_pkey_encrypt(key_id
, op_desc
,
359 asym_op_info
->src
, asym_op_info
->src_len
,
360 asym_op_info
->dst
, asym_op_info
->dst_len
);
362 ret
= qcrypto_akcipher_encrypt(akcipher
,
363 asym_op_info
->src
, asym_op_info
->src_len
,
364 asym_op_info
->dst
, asym_op_info
->dst_len
, &local_error
);
368 case VIRTIO_CRYPTO_AKCIPHER_DECRYPT
:
370 ret
= keyctl_pkey_decrypt(key_id
, op_desc
,
371 asym_op_info
->src
, asym_op_info
->src_len
,
372 asym_op_info
->dst
, asym_op_info
->dst_len
);
374 ret
= qcrypto_akcipher_decrypt(akcipher
,
375 asym_op_info
->src
, asym_op_info
->src_len
,
376 asym_op_info
->dst
, asym_op_info
->dst_len
, &local_error
);
380 case VIRTIO_CRYPTO_AKCIPHER_SIGN
:
382 ret
= keyctl_pkey_sign(key_id
, op_desc
,
383 asym_op_info
->src
, asym_op_info
->src_len
,
384 asym_op_info
->dst
, asym_op_info
->dst_len
);
386 ret
= qcrypto_akcipher_sign(akcipher
,
387 asym_op_info
->src
, asym_op_info
->src_len
,
388 asym_op_info
->dst
, asym_op_info
->dst_len
, &local_error
);
392 case VIRTIO_CRYPTO_AKCIPHER_VERIFY
:
394 ret
= keyctl_pkey_verify(key_id
, op_desc
,
395 asym_op_info
->src
, asym_op_info
->src_len
,
396 asym_op_info
->dst
, asym_op_info
->dst_len
);
398 ret
= qcrypto_akcipher_verify(akcipher
,
399 asym_op_info
->src
, asym_op_info
->src_len
,
400 asym_op_info
->dst
, asym_op_info
->dst_len
, &local_error
);
405 error_setg(&local_error
, "Unknown opcode: %u", op_code
);
406 status
= -VIRTIO_CRYPTO_ERR
;
412 if (errno
!= EKEYREJECTED
) {
413 error_report("Failed do operation with keyctl: %d", errno
);
416 error_report_err(local_error
);
418 status
= op_code
== VIRTIO_CRYPTO_AKCIPHER_VERIFY
?
419 -VIRTIO_CRYPTO_KEY_REJECTED
: -VIRTIO_CRYPTO_ERR
;
421 status
= VIRTIO_CRYPTO_OK
;
422 asym_op_info
->dst_len
= ret
;
427 keyctl_unlink(key_id
, KCTL_KEY_RING
);
429 task
->status
= status
;
431 qemu_mutex_lock(&task
->lkcf
->rsp_mutex
);
432 if (QSIMPLEQ_EMPTY(&task
->lkcf
->responses
)) {
435 QSIMPLEQ_INSERT_TAIL(&task
->lkcf
->responses
, task
, queue
);
436 qemu_mutex_unlock(&task
->lkcf
->rsp_mutex
);
439 eventfd_write(task
->lkcf
->eventfd
, 1);
443 static void *cryptodev_lkcf_worker(void *arg
)
445 CryptoDevBackendLKCF
*backend
= (CryptoDevBackendLKCF
*)arg
;
446 CryptoDevLKCFTask
*task
;
450 qemu_mutex_lock(&backend
->mutex
);
451 while (backend
->running
&& QSIMPLEQ_EMPTY(&backend
->requests
)) {
452 qemu_cond_wait(&backend
->cond
, &backend
->mutex
);
454 if (backend
->running
) {
455 task
= QSIMPLEQ_FIRST(&backend
->requests
);
456 QSIMPLEQ_REMOVE_HEAD(&backend
->requests
, queue
);
458 qemu_mutex_unlock(&backend
->mutex
);
464 cryptodev_lkcf_execute_task(task
);
470 static int cryptodev_lkcf_operation(
471 CryptoDevBackend
*backend
,
472 CryptoDevBackendOpInfo
*op_info
)
474 CryptoDevBackendLKCF
*lkcf
=
475 CRYPTODEV_BACKEND_LKCF(backend
);
476 CryptoDevBackendLKCFSession
*sess
;
477 QCryptodevBackendAlgType algtype
= op_info
->algtype
;
478 CryptoDevLKCFTask
*task
;
480 if (op_info
->session_id
>= MAX_SESSIONS
||
481 lkcf
->sess
[op_info
->session_id
] == NULL
) {
482 error_report("Cannot find a valid session id: %" PRIu64
"",
483 op_info
->session_id
);
484 return -VIRTIO_CRYPTO_INVSESS
;
487 sess
= lkcf
->sess
[op_info
->session_id
];
488 if (algtype
!= QCRYPTODEV_BACKEND_ALG_ASYM
) {
489 error_report("algtype not supported: %u", algtype
);
490 return -VIRTIO_CRYPTO_NOTSUPP
;
493 task
= g_new0(CryptoDevLKCFTask
, 1);
494 task
->op_info
= op_info
;
495 task
->cb
= op_info
->cb
;
496 task
->opaque
= op_info
->opaque
;
499 task
->status
= -VIRTIO_CRYPTO_ERR
;
501 qemu_mutex_lock(&lkcf
->mutex
);
502 QSIMPLEQ_INSERT_TAIL(&lkcf
->requests
, task
, queue
);
503 qemu_mutex_unlock(&lkcf
->mutex
);
504 qemu_cond_signal(&lkcf
->cond
);
506 return VIRTIO_CRYPTO_OK
;
509 static int cryptodev_lkcf_create_asym_session(
510 CryptoDevBackendLKCF
*lkcf
,
511 CryptoDevBackendAsymSessionInfo
*sess_info
,
512 uint64_t *session_id
)
514 Error
*local_error
= NULL
;
516 g_autofree CryptoDevBackendLKCFSession
*sess
=
517 g_new0(CryptoDevBackendLKCFSession
, 1);
519 switch (sess_info
->algo
) {
520 case VIRTIO_CRYPTO_AKCIPHER_RSA
:
521 sess
->akcipher_opts
.alg
= QCRYPTO_AKCIPHER_ALG_RSA
;
522 if (cryptodev_lkcf_set_rsa_opt(
523 sess_info
->u
.rsa
.padding_algo
, sess_info
->u
.rsa
.hash_algo
,
524 &sess
->akcipher_opts
.u
.rsa
, &local_error
) != 0) {
525 error_report_err(local_error
);
526 return -VIRTIO_CRYPTO_ERR
;
531 error_report("Unsupported asym alg %u", sess_info
->algo
);
532 return -VIRTIO_CRYPTO_NOTSUPP
;
535 switch (sess_info
->keytype
) {
536 case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC
:
537 sess
->keytype
= QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC
;
540 case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE
:
541 sess
->keytype
= QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE
;
545 error_report("Unknown akcipher keytype: %u", sess_info
->keytype
);
546 return -VIRTIO_CRYPTO_ERR
;
549 index
= cryptodev_lkcf_get_unused_session_index(lkcf
);
551 error_report("Total number of sessions created exceeds %u",
553 return -VIRTIO_CRYPTO_ERR
;
556 sess
->keylen
= sess_info
->keylen
;
557 sess
->key
= g_malloc(sess_info
->keylen
);
558 memcpy(sess
->key
, sess_info
->key
, sess_info
->keylen
);
560 lkcf
->sess
[index
] = g_steal_pointer(&sess
);
563 return VIRTIO_CRYPTO_OK
;
566 static int cryptodev_lkcf_create_session(
567 CryptoDevBackend
*backend
,
568 CryptoDevBackendSessionInfo
*sess_info
,
569 uint32_t queue_index
,
570 CryptoDevCompletionFunc cb
,
573 CryptoDevBackendAsymSessionInfo
*asym_sess_info
;
574 CryptoDevBackendLKCF
*lkcf
=
575 CRYPTODEV_BACKEND_LKCF(backend
);
578 switch (sess_info
->op_code
) {
579 case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION
:
580 asym_sess_info
= &sess_info
->u
.asym_sess_info
;
581 ret
= cryptodev_lkcf_create_asym_session(
582 lkcf
, asym_sess_info
, &sess_info
->session_id
);
586 ret
= -VIRTIO_CRYPTO_NOTSUPP
;
587 error_report("Unsupported opcode: %" PRIu32
"",
597 static int cryptodev_lkcf_close_session(CryptoDevBackend
*backend
,
599 uint32_t queue_index
,
600 CryptoDevCompletionFunc cb
,
603 CryptoDevBackendLKCF
*lkcf
= CRYPTODEV_BACKEND_LKCF(backend
);
604 CryptoDevBackendLKCFSession
*session
;
606 assert(session_id
< MAX_SESSIONS
&& lkcf
->sess
[session_id
]);
607 session
= lkcf
->sess
[session_id
];
608 lkcf
->sess
[session_id
] = NULL
;
610 g_free(session
->key
);
614 cb(opaque
, VIRTIO_CRYPTO_OK
);
619 static void cryptodev_lkcf_class_init(ObjectClass
*oc
, void *data
)
621 CryptoDevBackendClass
*bc
= CRYPTODEV_BACKEND_CLASS(oc
);
623 bc
->init
= cryptodev_lkcf_init
;
624 bc
->cleanup
= cryptodev_lkcf_cleanup
;
625 bc
->create_session
= cryptodev_lkcf_create_session
;
626 bc
->close_session
= cryptodev_lkcf_close_session
;
627 bc
->do_op
= cryptodev_lkcf_operation
;
630 static const TypeInfo cryptodev_builtin_info
= {
631 .name
= TYPE_CRYPTODEV_BACKEND_LKCF
,
632 .parent
= TYPE_CRYPTODEV_BACKEND
,
633 .class_init
= cryptodev_lkcf_class_init
,
634 .instance_size
= sizeof(CryptoDevBackendLKCF
),
637 static void cryptodev_lkcf_register_types(void)
639 type_register_static(&cryptodev_builtin_info
);
642 type_init(cryptodev_lkcf_register_types
);