xref: /openbmc/qemu/backends/cryptodev-builtin.c (revision d2dfe0b5)
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.1 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 #include "crypto/akcipher.h"
30 #include "qom/object.h"
31 
32 
33 /**
34  * @TYPE_CRYPTODEV_BACKEND_BUILTIN:
35  * name of backend that uses QEMU cipher API
36  */
37 #define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin"
38 
39 OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN)
40 
41 
42 typedef struct CryptoDevBackendBuiltinSession {
43     QCryptoCipher *cipher;
44     uint8_t direction; /* encryption or decryption */
45     uint8_t type; /* cipher? hash? aead? */
46     QCryptoAkCipher *akcipher;
47     QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
48 } CryptoDevBackendBuiltinSession;
49 
50 /* Max number of symmetric/asymmetric sessions */
51 #define MAX_NUM_SESSIONS 256
52 
53 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN    512
54 #define CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN  64
55 
56 struct CryptoDevBackendBuiltin {
57     CryptoDevBackend parent_obj;
58 
59     CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS];
60 };
61 
62 static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend)
63 {
64     QCryptoAkCipherOptions opts;
65 
66     opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
67     opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
68     if (qcrypto_akcipher_supports(&opts)) {
69         backend->conf.crypto_services |=
70                      (1u << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER);
71         backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
72     }
73 }
74 
75 static void cryptodev_builtin_init(
76              CryptoDevBackend *backend, Error **errp)
77 {
78     /* Only support one queue */
79     int queues = backend->conf.peers.queues;
80     CryptoDevBackendClient *cc;
81 
82     if (queues != 1) {
83         error_setg(errp,
84                   "Only support one queue in cryptdov-builtin backend");
85         return;
86     }
87 
88     cc = cryptodev_backend_new_client();
89     cc->info_str = g_strdup_printf("cryptodev-builtin0");
90     cc->queue_index = 0;
91     cc->type = QCRYPTODEV_BACKEND_TYPE_BUILTIN;
92     backend->conf.peers.ccs[0] = cc;
93 
94     backend->conf.crypto_services =
95                          1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER |
96                          1u << QCRYPTODEV_BACKEND_SERVICE_HASH |
97                          1u << QCRYPTODEV_BACKEND_SERVICE_MAC;
98     backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
99     backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
100     /*
101      * Set the Maximum length of crypto request.
102      * Why this value? Just avoid to overflow when
103      * memory allocation for each crypto request.
104      */
105     backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
106     backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
107     backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
108     cryptodev_builtin_init_akcipher(backend);
109 
110     cryptodev_backend_set_ready(backend, true);
111 }
112 
113 static int
114 cryptodev_builtin_get_unused_session_index(
115                  CryptoDevBackendBuiltin *builtin)
116 {
117     size_t i;
118 
119     for (i = 0; i < MAX_NUM_SESSIONS; i++) {
120         if (builtin->sessions[i] == NULL) {
121             return i;
122         }
123     }
124 
125     return -1;
126 }
127 
128 #define AES_KEYSIZE_128 16
129 #define AES_KEYSIZE_192 24
130 #define AES_KEYSIZE_256 32
131 #define AES_KEYSIZE_128_XTS AES_KEYSIZE_256
132 #define AES_KEYSIZE_256_XTS 64
133 
134 static int
135 cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp)
136 {
137     int algo;
138 
139     if (key_len == AES_KEYSIZE_128) {
140         algo = QCRYPTO_CIPHER_ALG_AES_128;
141     } else if (key_len == AES_KEYSIZE_192) {
142         algo = QCRYPTO_CIPHER_ALG_AES_192;
143     } else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */
144         if (mode == QCRYPTO_CIPHER_MODE_XTS) {
145             algo = QCRYPTO_CIPHER_ALG_AES_128;
146         } else {
147             algo = QCRYPTO_CIPHER_ALG_AES_256;
148         }
149     } else if (key_len == AES_KEYSIZE_256_XTS) {
150         if (mode == QCRYPTO_CIPHER_MODE_XTS) {
151             algo = QCRYPTO_CIPHER_ALG_AES_256;
152         } else {
153             goto err;
154         }
155     } else {
156         goto err;
157     }
158 
159     return algo;
160 
161 err:
162    error_setg(errp, "Unsupported key length :%u", key_len);
163    return -1;
164 }
165 
166 static int cryptodev_builtin_get_rsa_hash_algo(
167     int virtio_rsa_hash, Error **errp)
168 {
169     switch (virtio_rsa_hash) {
170     case VIRTIO_CRYPTO_RSA_MD5:
171         return QCRYPTO_HASH_ALG_MD5;
172 
173     case VIRTIO_CRYPTO_RSA_SHA1:
174         return QCRYPTO_HASH_ALG_SHA1;
175 
176     case VIRTIO_CRYPTO_RSA_SHA256:
177         return QCRYPTO_HASH_ALG_SHA256;
178 
179     case VIRTIO_CRYPTO_RSA_SHA512:
180         return QCRYPTO_HASH_ALG_SHA512;
181 
182     default:
183         error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
184         return -1;
185     }
186 }
187 
188 static int cryptodev_builtin_set_rsa_options(
189                     int virtio_padding_algo,
190                     int virtio_hash_algo,
191                     QCryptoAkCipherOptionsRSA *opt,
192                     Error **errp)
193 {
194     if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
195         int hash_alg;
196 
197         hash_alg = cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
198         if (hash_alg < 0) {
199             return -1;
200         }
201         opt->hash_alg = hash_alg;
202         opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
203         return 0;
204     }
205 
206     if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
207         opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
208         return 0;
209     }
210 
211     error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
212     return -1;
213 }
214 
215 static int cryptodev_builtin_create_cipher_session(
216                     CryptoDevBackendBuiltin *builtin,
217                     CryptoDevBackendSymSessionInfo *sess_info,
218                     Error **errp)
219 {
220     int algo;
221     int mode;
222     QCryptoCipher *cipher;
223     int index;
224     CryptoDevBackendBuiltinSession *sess;
225 
226     if (sess_info->op_type != VIRTIO_CRYPTO_SYM_OP_CIPHER) {
227         error_setg(errp, "Unsupported optype :%u", sess_info->op_type);
228         return -1;
229     }
230 
231     index = cryptodev_builtin_get_unused_session_index(builtin);
232     if (index < 0) {
233         error_setg(errp, "Total number of sessions created exceeds %u",
234                   MAX_NUM_SESSIONS);
235         return -1;
236     }
237 
238     switch (sess_info->cipher_alg) {
239     case VIRTIO_CRYPTO_CIPHER_AES_ECB:
240         mode = QCRYPTO_CIPHER_MODE_ECB;
241         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
242                                                     mode, errp);
243         if (algo < 0)  {
244             return -1;
245         }
246         break;
247     case VIRTIO_CRYPTO_CIPHER_AES_CBC:
248         mode = QCRYPTO_CIPHER_MODE_CBC;
249         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
250                                                     mode, errp);
251         if (algo < 0)  {
252             return -1;
253         }
254         break;
255     case VIRTIO_CRYPTO_CIPHER_AES_CTR:
256         mode = QCRYPTO_CIPHER_MODE_CTR;
257         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
258                                                     mode, errp);
259         if (algo < 0)  {
260             return -1;
261         }
262         break;
263     case VIRTIO_CRYPTO_CIPHER_AES_XTS:
264         mode = QCRYPTO_CIPHER_MODE_XTS;
265         algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
266                                                     mode, errp);
267         if (algo < 0)  {
268             return -1;
269         }
270         break;
271     case VIRTIO_CRYPTO_CIPHER_3DES_ECB:
272         mode = QCRYPTO_CIPHER_MODE_ECB;
273         algo = QCRYPTO_CIPHER_ALG_3DES;
274         break;
275     case VIRTIO_CRYPTO_CIPHER_3DES_CBC:
276         mode = QCRYPTO_CIPHER_MODE_CBC;
277         algo = QCRYPTO_CIPHER_ALG_3DES;
278         break;
279     case VIRTIO_CRYPTO_CIPHER_3DES_CTR:
280         mode = QCRYPTO_CIPHER_MODE_CTR;
281         algo = QCRYPTO_CIPHER_ALG_3DES;
282         break;
283     default:
284         error_setg(errp, "Unsupported cipher alg :%u",
285                    sess_info->cipher_alg);
286         return -1;
287     }
288 
289     cipher = qcrypto_cipher_new(algo, mode,
290                                sess_info->cipher_key,
291                                sess_info->key_len,
292                                errp);
293     if (!cipher) {
294         return -1;
295     }
296 
297     sess = g_new0(CryptoDevBackendBuiltinSession, 1);
298     sess->cipher = cipher;
299     sess->direction = sess_info->direction;
300     sess->type = sess_info->op_type;
301 
302     builtin->sessions[index] = sess;
303 
304     return index;
305 }
306 
307 static int cryptodev_builtin_create_akcipher_session(
308                     CryptoDevBackendBuiltin *builtin,
309                     CryptoDevBackendAsymSessionInfo *sess_info,
310                     Error **errp)
311 {
312     CryptoDevBackendBuiltinSession *sess;
313     QCryptoAkCipher *akcipher;
314     int index;
315     QCryptoAkCipherKeyType type;
316     QCryptoAkCipherOptions opts;
317 
318     switch (sess_info->algo) {
319     case VIRTIO_CRYPTO_AKCIPHER_RSA:
320         opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
321         if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo,
322             sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) {
323             return -1;
324         }
325         break;
326 
327     /* TODO support DSA&ECDSA until qemu crypto framework support these */
328 
329     default:
330         error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo);
331         return -1;
332     }
333 
334     switch (sess_info->keytype) {
335     case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
336         type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
337         break;
338 
339     case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
340         type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
341         break;
342 
343     default:
344         error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype);
345         return -1;
346     }
347 
348     index = cryptodev_builtin_get_unused_session_index(builtin);
349     if (index < 0) {
350         error_setg(errp, "Total number of sessions created exceeds %u",
351                    MAX_NUM_SESSIONS);
352         return -1;
353     }
354 
355     akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key,
356                                     sess_info->keylen, errp);
357     if (!akcipher) {
358         return -1;
359     }
360 
361     sess = g_new0(CryptoDevBackendBuiltinSession, 1);
362     sess->akcipher = akcipher;
363 
364     builtin->sessions[index] = sess;
365 
366     return index;
367 }
368 
369 static int cryptodev_builtin_create_session(
370            CryptoDevBackend *backend,
371            CryptoDevBackendSessionInfo *sess_info,
372            uint32_t queue_index,
373            CryptoDevCompletionFunc cb,
374            void *opaque)
375 {
376     CryptoDevBackendBuiltin *builtin =
377                       CRYPTODEV_BACKEND_BUILTIN(backend);
378     CryptoDevBackendSymSessionInfo *sym_sess_info;
379     CryptoDevBackendAsymSessionInfo *asym_sess_info;
380     int ret, status;
381     Error *local_error = NULL;
382 
383     switch (sess_info->op_code) {
384     case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
385         sym_sess_info = &sess_info->u.sym_sess_info;
386         ret = cryptodev_builtin_create_cipher_session(
387                     builtin, sym_sess_info, &local_error);
388         break;
389 
390     case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
391         asym_sess_info = &sess_info->u.asym_sess_info;
392         ret = cryptodev_builtin_create_akcipher_session(
393                            builtin, asym_sess_info, &local_error);
394         break;
395 
396     case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
397     case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
398     default:
399         error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
400                    sess_info->op_code);
401         return -VIRTIO_CRYPTO_NOTSUPP;
402     }
403 
404     if (local_error) {
405         error_report_err(local_error);
406     }
407     if (ret < 0) {
408         status = -VIRTIO_CRYPTO_ERR;
409     } else {
410         sess_info->session_id = ret;
411         status = VIRTIO_CRYPTO_OK;
412     }
413     if (cb) {
414         cb(opaque, status);
415     }
416     return 0;
417 }
418 
419 static int cryptodev_builtin_close_session(
420            CryptoDevBackend *backend,
421            uint64_t session_id,
422            uint32_t queue_index,
423            CryptoDevCompletionFunc cb,
424            void *opaque)
425 {
426     CryptoDevBackendBuiltin *builtin =
427                       CRYPTODEV_BACKEND_BUILTIN(backend);
428     CryptoDevBackendBuiltinSession *session;
429 
430     assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
431 
432     session = builtin->sessions[session_id];
433     if (session->cipher) {
434         qcrypto_cipher_free(session->cipher);
435     } else if (session->akcipher) {
436         qcrypto_akcipher_free(session->akcipher);
437     }
438 
439     g_free(session);
440     builtin->sessions[session_id] = NULL;
441     if (cb) {
442         cb(opaque, VIRTIO_CRYPTO_OK);
443     }
444     return 0;
445 }
446 
447 static int cryptodev_builtin_sym_operation(
448                  CryptoDevBackendBuiltinSession *sess,
449                  CryptoDevBackendSymOpInfo *op_info, Error **errp)
450 {
451     int ret;
452 
453     if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
454         error_setg(errp,
455                "Algorithm chain is unsupported for cryptdoev-builtin");
456         return -VIRTIO_CRYPTO_NOTSUPP;
457     }
458 
459     if (op_info->iv_len > 0) {
460         ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
461                                    op_info->iv_len, errp);
462         if (ret < 0) {
463             return -VIRTIO_CRYPTO_ERR;
464         }
465     }
466 
467     if (sess->direction == VIRTIO_CRYPTO_OP_ENCRYPT) {
468         ret = qcrypto_cipher_encrypt(sess->cipher, op_info->src,
469                                      op_info->dst, op_info->src_len, errp);
470         if (ret < 0) {
471             return -VIRTIO_CRYPTO_ERR;
472         }
473     } else {
474         ret = qcrypto_cipher_decrypt(sess->cipher, op_info->src,
475                                      op_info->dst, op_info->src_len, errp);
476         if (ret < 0) {
477             return -VIRTIO_CRYPTO_ERR;
478         }
479     }
480 
481     return VIRTIO_CRYPTO_OK;
482 }
483 
484 static int cryptodev_builtin_asym_operation(
485                  CryptoDevBackendBuiltinSession *sess, uint32_t op_code,
486                  CryptoDevBackendAsymOpInfo *op_info, Error **errp)
487 {
488     int ret;
489 
490     switch (op_code) {
491     case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
492         ret = qcrypto_akcipher_encrypt(sess->akcipher,
493                                        op_info->src, op_info->src_len,
494                                        op_info->dst, op_info->dst_len, errp);
495         break;
496 
497     case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
498         ret = qcrypto_akcipher_decrypt(sess->akcipher,
499                                        op_info->src, op_info->src_len,
500                                        op_info->dst, op_info->dst_len, errp);
501         break;
502 
503     case VIRTIO_CRYPTO_AKCIPHER_SIGN:
504         ret = qcrypto_akcipher_sign(sess->akcipher,
505                                     op_info->src, op_info->src_len,
506                                     op_info->dst, op_info->dst_len, errp);
507         break;
508 
509     case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
510         ret = qcrypto_akcipher_verify(sess->akcipher,
511                                       op_info->src, op_info->src_len,
512                                       op_info->dst, op_info->dst_len, errp);
513         break;
514 
515     default:
516         return -VIRTIO_CRYPTO_ERR;
517     }
518 
519     if (ret < 0) {
520         if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
521             return -VIRTIO_CRYPTO_KEY_REJECTED;
522         }
523         return -VIRTIO_CRYPTO_ERR;
524     }
525 
526     /* Buffer is too short, typically the driver should handle this case */
527     if (unlikely(ret > op_info->dst_len)) {
528         if (errp && !*errp) {
529             error_setg(errp, "dst buffer too short");
530         }
531 
532         return -VIRTIO_CRYPTO_ERR;
533     }
534 
535     op_info->dst_len = ret;
536 
537     return VIRTIO_CRYPTO_OK;
538 }
539 
540 static int cryptodev_builtin_operation(
541                  CryptoDevBackend *backend,
542                  CryptoDevBackendOpInfo *op_info)
543 {
544     CryptoDevBackendBuiltin *builtin =
545                       CRYPTODEV_BACKEND_BUILTIN(backend);
546     CryptoDevBackendBuiltinSession *sess;
547     CryptoDevBackendSymOpInfo *sym_op_info;
548     CryptoDevBackendAsymOpInfo *asym_op_info;
549     QCryptodevBackendAlgType algtype = op_info->algtype;
550     int status = -VIRTIO_CRYPTO_ERR;
551     Error *local_error = NULL;
552 
553     if (op_info->session_id >= MAX_NUM_SESSIONS ||
554               builtin->sessions[op_info->session_id] == NULL) {
555         error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
556                    op_info->session_id);
557         return -VIRTIO_CRYPTO_INVSESS;
558     }
559 
560     sess = builtin->sessions[op_info->session_id];
561     if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) {
562         sym_op_info = op_info->u.sym_op_info;
563         status = cryptodev_builtin_sym_operation(sess, sym_op_info,
564                                                  &local_error);
565     } else if (algtype == QCRYPTODEV_BACKEND_ALG_ASYM) {
566         asym_op_info = op_info->u.asym_op_info;
567         status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
568                                                   asym_op_info, &local_error);
569     }
570 
571     if (local_error) {
572         error_report_err(local_error);
573     }
574     if (op_info->cb) {
575         op_info->cb(op_info->opaque, status);
576     }
577     return 0;
578 }
579 
580 static void cryptodev_builtin_cleanup(
581              CryptoDevBackend *backend,
582              Error **errp)
583 {
584     CryptoDevBackendBuiltin *builtin =
585                       CRYPTODEV_BACKEND_BUILTIN(backend);
586     size_t i;
587     int queues = backend->conf.peers.queues;
588     CryptoDevBackendClient *cc;
589 
590     for (i = 0; i < MAX_NUM_SESSIONS; i++) {
591         if (builtin->sessions[i] != NULL) {
592             cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
593         }
594     }
595 
596     for (i = 0; i < queues; i++) {
597         cc = backend->conf.peers.ccs[i];
598         if (cc) {
599             cryptodev_backend_free_client(cc);
600             backend->conf.peers.ccs[i] = NULL;
601         }
602     }
603 
604     cryptodev_backend_set_ready(backend, false);
605 }
606 
607 static void
608 cryptodev_builtin_class_init(ObjectClass *oc, void *data)
609 {
610     CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
611 
612     bc->init = cryptodev_builtin_init;
613     bc->cleanup = cryptodev_builtin_cleanup;
614     bc->create_session = cryptodev_builtin_create_session;
615     bc->close_session = cryptodev_builtin_close_session;
616     bc->do_op = cryptodev_builtin_operation;
617 }
618 
619 static const TypeInfo cryptodev_builtin_info = {
620     .name = TYPE_CRYPTODEV_BACKEND_BUILTIN,
621     .parent = TYPE_CRYPTODEV_BACKEND,
622     .class_init = cryptodev_builtin_class_init,
623     .instance_size = sizeof(CryptoDevBackendBuiltin),
624 };
625 
626 static void
627 cryptodev_builtin_register_types(void)
628 {
629     type_register_static(&cryptodev_builtin_info);
630 }
631 
632 type_init(cryptodev_builtin_register_types);
633