linux-crypto.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/9] Introduce akcipher service for virtio-crypto
@ 2022-04-28 13:59 zhenwei pi
  2022-04-28 13:59 ` [PATCH v5 1/9] virtio-crypto: header update zhenwei pi
                   ` (9 more replies)
  0 siblings, 10 replies; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

Hi, Lei & MST

Daniel has started to review the akcipher framework and nettle & gcrypt
implementation, this part seems to be ready soon. Thanks a lot to Daniel!

And the last patch "crypto: Introduce RSA algorithm" handles akcipher
requests from guest and uses the new akcipher service. The new feature
can be used to test by the builtin driver. I would appreciate it if you
could review patch.

v4 -> v5:
- Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
- Rename asn1_decoder.c to der.c.
- Code style fix: use 'cleanup' & 'error' lables.
- Allow autoptr type to auto-free.
- Add test cases for rsakey to handle DER error.
- Other minor fixes.

v3 -> v4:
- Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
XXX-alg -> XXX-algo.
- Change version info in qapi/crypto.json, from 7.0 -> 7.1.
- Remove ecdsa from qapi/crypto.json, it would be introduced with the implemetion later.
- Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in qapi/crypto.json.
- Rename arguments of qcrypto_akcipher_XXX to keep aligned with qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add qcrypto_akcipher_max_XXX APIs.
- Add new API: qcrypto_akcipher_supports.
- Change the return value of qcrypto_akcipher_enc/dec/sign, these functions return the actual length of result.
- Separate ASN.1 source code and test case clean.
- Disable RSA raw encoding for akcipher-nettle.
- Separate RSA key parser into rsakey.{hc}, and implememts it with builtin-asn1-decoder and nettle respectivly.
- Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher priority than nettle.
- For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of returned result maybe less than the dst buffer size, return the actual length of result instead of the buffer length to the guest side. (in function virtio_crypto_akcipher_input_data_helper)
- Other minor changes.

Thanks to Daniel!

Eric pointed out this missing part of use case, send it here again.

In our plan, the feature is designed for HTTPS offloading case and other applications which use kernel RSA/ecdsa by keyctl syscall. The full picture shows bellow:


                  Nginx/openssl[1] ... Apps
Guest   -----------------------------------------
                   virtio-crypto driver[2]
-------------------------------------------------
                   virtio-crypto backend[3]
Host    -----------------------------------------
                  /          |          \
              builtin[4]   vhost     keyctl[5] ...


[1] User applications can offload RSA calculation to kernel by keyctl syscall. There is no keyctl engine in openssl currently, we developed a engine and tried to contribute it to openssl upstream, but openssl 1.x does not accept new feature. Link:
    https://github.com/openssl/openssl/pull/16689

This branch is available and maintained by Lei <helei.sig11@bytedance.com>
    https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine

We tested nginx(change config file only) with openssl keyctl engine, it works fine.

[2] virtio-crypto driver is used to communicate with host side, send requests to host side to do asymmetric calculation.
    https://lkml.org/lkml/2022/3/1/1425

[3] virtio-crypto backend handles requests from guest side, and forwards request to crypto backend driver of QEMU.

[4] Currently RSA is supported only in builtin driver. This driver is supposed to test the full feature without other software(Ex vhost process) and hardware dependence. ecdsa is introduced into qapi type without implementation, this may be implemented in Q3-2022 or later. If ecdsa type definition should be added with the implementation together, I'll remove this in next version.

[5] keyctl backend is in development, we will post this feature in Q2-2022. keyctl backend can use hardware acceleration(Ex, Intel QAT).

Setup the full environment, tested with Intel QAT on host side, the QPS of HTTPS increase to ~200% in a guest.

VS PCI passthrough: the most important benefit of this solution makes the VM migratable.

v2 -> v3:
- Introduce akcipher types to qapi
- Add test/benchmark suite for akcipher class
- Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
  - crypto: Introduce akcipher crypto class
  - virtio-crypto: Introduce RSA algorithm

v1 -> v2:
- Update virtio_crypto.h from v2 version of related kernel patch.

v1:
- Support akcipher for virtio-crypto.
- Introduce akcipher class.
- Introduce ASN1 decoder into QEMU.
- Implement RSA backend by nettle/hogweed.

Lei He (6):
  qapi: crypto-akcipher: Introduce akcipher types to qapi
  crypto: add ASN.1 DER decoder
  crypto: Implement RSA algorithm by hogweed
  crypto: Implement RSA algorithm by gcrypt
  test/crypto: Add test suite for crypto akcipher
  tests/crypto: Add test suite for RSA keys

Zhenwei Pi (3):
  virtio-crypto: header update
  crypto: Introduce akcipher crypto class
  crypto: Introduce RSA algorithm

 backends/cryptodev-builtin.c                  | 272 ++++-
 backends/cryptodev-vhost-user.c               |  34 +-
 backends/cryptodev.c                          |  32 +-
 crypto/akcipher-gcrypt.c.inc                  | 520 +++++++++
 crypto/akcipher-nettle.c.inc                  | 432 ++++++++
 crypto/akcipher.c                             | 108 ++
 crypto/akcipherpriv.h                         |  55 +
 crypto/der.c                                  | 190 ++++
 crypto/der.h                                  |  82 ++
 crypto/meson.build                            |   6 +
 crypto/rsakey-builtin.c.inc                   | 209 ++++
 crypto/rsakey-nettle.c.inc                    | 154 +++
 crypto/rsakey.c                               |  44 +
 crypto/rsakey.h                               |  94 ++
 hw/virtio/virtio-crypto.c                     | 323 ++++--
 include/crypto/akcipher.h                     | 158 +++
 include/hw/virtio/virtio-crypto.h             |   5 +-
 .../standard-headers/linux/virtio_crypto.h    |  82 +-
 include/sysemu/cryptodev.h                    |  83 +-
 meson.build                                   |  11 +
 qapi/crypto.json                              |  64 ++
 tests/bench/benchmark-crypto-akcipher.c       | 157 +++
 tests/bench/meson.build                       |   4 +
 tests/bench/test_akcipher_keys.inc            | 537 ++++++++++
 tests/unit/meson.build                        |   2 +
 tests/unit/test-crypto-akcipher.c             | 990 ++++++++++++++++++
 tests/unit/test-crypto-der.c                  | 290 +++++
 27 files changed, 4792 insertions(+), 146 deletions(-)
 create mode 100644 crypto/akcipher-gcrypt.c.inc
 create mode 100644 crypto/akcipher-nettle.c.inc
 create mode 100644 crypto/akcipher.c
 create mode 100644 crypto/akcipherpriv.h
 create mode 100644 crypto/der.c
 create mode 100644 crypto/der.h
 create mode 100644 crypto/rsakey-builtin.c.inc
 create mode 100644 crypto/rsakey-nettle.c.inc
 create mode 100644 crypto/rsakey.c
 create mode 100644 crypto/rsakey.h
 create mode 100644 include/crypto/akcipher.h
 create mode 100644 tests/bench/benchmark-crypto-akcipher.c
 create mode 100644 tests/bench/test_akcipher_keys.inc
 create mode 100644 tests/unit/test-crypto-akcipher.c
 create mode 100644 tests/unit/test-crypto-der.c

-- 
2.20.1


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v5 1/9] virtio-crypto: header update
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-12  9:55   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

Update header from linux, support akcipher service.

Reviewed-by: Gonglei <arei.gonglei@huawei.com>
Signed-off-by: lei he <helei.sig11@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
---
 .../standard-headers/linux/virtio_crypto.h    | 82 ++++++++++++++++++-
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/include/standard-headers/linux/virtio_crypto.h b/include/standard-headers/linux/virtio_crypto.h
index 5ff0b4ee59..68066dafb6 100644
--- a/include/standard-headers/linux/virtio_crypto.h
+++ b/include/standard-headers/linux/virtio_crypto.h
@@ -37,6 +37,7 @@
 #define VIRTIO_CRYPTO_SERVICE_HASH   1
 #define VIRTIO_CRYPTO_SERVICE_MAC    2
 #define VIRTIO_CRYPTO_SERVICE_AEAD   3
+#define VIRTIO_CRYPTO_SERVICE_AKCIPHER 4
 
 #define VIRTIO_CRYPTO_OPCODE(service, op)   (((service) << 8) | (op))
 
@@ -57,6 +58,10 @@ struct virtio_crypto_ctrl_header {
 	   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x02)
 #define VIRTIO_CRYPTO_AEAD_DESTROY_SESSION \
 	   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x03)
+#define VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION \
+	   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x04)
+#define VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION \
+	   VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x05)
 	uint32_t opcode;
 	uint32_t algo;
 	uint32_t flag;
@@ -180,6 +185,58 @@ struct virtio_crypto_aead_create_session_req {
 	uint8_t padding[32];
 };
 
+struct virtio_crypto_rsa_session_para {
+#define VIRTIO_CRYPTO_RSA_RAW_PADDING   0
+#define VIRTIO_CRYPTO_RSA_PKCS1_PADDING 1
+	uint32_t padding_algo;
+
+#define VIRTIO_CRYPTO_RSA_NO_HASH   0
+#define VIRTIO_CRYPTO_RSA_MD2       1
+#define VIRTIO_CRYPTO_RSA_MD3       2
+#define VIRTIO_CRYPTO_RSA_MD4       3
+#define VIRTIO_CRYPTO_RSA_MD5       4
+#define VIRTIO_CRYPTO_RSA_SHA1      5
+#define VIRTIO_CRYPTO_RSA_SHA256    6
+#define VIRTIO_CRYPTO_RSA_SHA384    7
+#define VIRTIO_CRYPTO_RSA_SHA512    8
+#define VIRTIO_CRYPTO_RSA_SHA224    9
+	uint32_t hash_algo;
+};
+
+struct virtio_crypto_ecdsa_session_para {
+#define VIRTIO_CRYPTO_CURVE_UNKNOWN   0
+#define VIRTIO_CRYPTO_CURVE_NIST_P192 1
+#define VIRTIO_CRYPTO_CURVE_NIST_P224 2
+#define VIRTIO_CRYPTO_CURVE_NIST_P256 3
+#define VIRTIO_CRYPTO_CURVE_NIST_P384 4
+#define VIRTIO_CRYPTO_CURVE_NIST_P521 5
+	uint32_t curve_id;
+	uint32_t padding;
+};
+
+struct virtio_crypto_akcipher_session_para {
+#define VIRTIO_CRYPTO_NO_AKCIPHER    0
+#define VIRTIO_CRYPTO_AKCIPHER_RSA   1
+#define VIRTIO_CRYPTO_AKCIPHER_DSA   2
+#define VIRTIO_CRYPTO_AKCIPHER_ECDSA 3
+	uint32_t algo;
+
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC  1
+#define VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE 2
+	uint32_t keytype;
+	uint32_t keylen;
+
+	union {
+		struct virtio_crypto_rsa_session_para rsa;
+		struct virtio_crypto_ecdsa_session_para ecdsa;
+	} u;
+};
+
+struct virtio_crypto_akcipher_create_session_req {
+	struct virtio_crypto_akcipher_session_para para;
+	uint8_t padding[36];
+};
+
 struct virtio_crypto_alg_chain_session_para {
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER  1
 #define VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH  2
@@ -247,6 +304,8 @@ struct virtio_crypto_op_ctrl_req {
 			mac_create_session;
 		struct virtio_crypto_aead_create_session_req
 			aead_create_session;
+		struct virtio_crypto_akcipher_create_session_req
+			akcipher_create_session;
 		struct virtio_crypto_destroy_session_req
 			destroy_session;
 		uint8_t padding[56];
@@ -266,6 +325,14 @@ struct virtio_crypto_op_header {
 	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x00)
 #define VIRTIO_CRYPTO_AEAD_DECRYPT \
 	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_ENCRYPT \
+	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00)
+#define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \
+	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01)
+#define VIRTIO_CRYPTO_AKCIPHER_SIGN \
+	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02)
+#define VIRTIO_CRYPTO_AKCIPHER_VERIFY \
+	VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x03)
 	uint32_t opcode;
 	/* algo should be service-specific algorithms */
 	uint32_t algo;
@@ -390,6 +457,16 @@ struct virtio_crypto_aead_data_req {
 	uint8_t padding[32];
 };
 
+struct virtio_crypto_akcipher_para {
+	uint32_t src_data_len;
+	uint32_t dst_data_len;
+};
+
+struct virtio_crypto_akcipher_data_req {
+	struct virtio_crypto_akcipher_para para;
+	uint8_t padding[40];
+};
+
 /* The request of the data virtqueue's packet */
 struct virtio_crypto_op_data_req {
 	struct virtio_crypto_op_header header;
@@ -399,6 +476,7 @@ struct virtio_crypto_op_data_req {
 		struct virtio_crypto_hash_data_req hash_req;
 		struct virtio_crypto_mac_data_req mac_req;
 		struct virtio_crypto_aead_data_req aead_req;
+		struct virtio_crypto_akcipher_data_req akcipher_req;
 		uint8_t padding[48];
 	} u;
 };
@@ -408,6 +486,8 @@ struct virtio_crypto_op_data_req {
 #define VIRTIO_CRYPTO_BADMSG    2
 #define VIRTIO_CRYPTO_NOTSUPP   3
 #define VIRTIO_CRYPTO_INVSESS   4 /* Invalid session id */
+#define VIRTIO_CRYPTO_NOSPC     5 /* no free session ID */
+#define VIRTIO_CRYPTO_KEY_REJECTED 6 /* Signature verification failed */
 
 /* The accelerator hardware is ready */
 #define VIRTIO_CRYPTO_S_HW_READY  (1 << 0)
@@ -438,7 +518,7 @@ struct virtio_crypto_config {
 	uint32_t max_cipher_key_len;
 	/* Maximum length of authenticated key */
 	uint32_t max_auth_key_len;
-	uint32_t reserve;
+	uint32_t akcipher_algo;
 	/* Maximum size of each crypto request's content */
 	uint64_t max_size;
 };
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
  2022-04-28 13:59 ` [PATCH v5 1/9] virtio-crypto: header update zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-04-28 13:59 ` [PATCH v5 3/9] crypto: Introduce akcipher crypto class zhenwei pi
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

From: Lei He <helei.sig11@bytedance.com>

Introduce akcipher types, also include RSA related types.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Lei He <helei.sig11@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
---
 qapi/crypto.json | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/qapi/crypto.json b/qapi/crypto.json
index 1ec54c15ca..f7bb9a42d0 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -540,3 +540,67 @@
   'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] },
             '*sanity-check': 'bool',
             '*passwordid': 'str' } }
+##
+# @QCryptoAkCipherAlgorithm:
+#
+# The supported algorithms for asymmetric encryption ciphers
+#
+# @rsa: RSA algorithm
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherAlgorithm',
+  'prefix': 'QCRYPTO_AKCIPHER_ALG',
+  'data': ['rsa']}
+
+##
+# @QCryptoAkCipherKeyType:
+#
+# The type of asymmetric keys.
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoAkCipherKeyType',
+  'prefix': 'QCRYPTO_AKCIPHER_KEY_TYPE',
+  'data': ['public', 'private']}
+
+##
+# @QCryptoRSAPaddingAlgorithm:
+#
+# The padding algorithm for RSA.
+#
+# @raw: no padding used
+# @pkcs1: pkcs1#v1.5
+#
+# Since: 7.1
+##
+{ 'enum': 'QCryptoRSAPaddingAlgorithm',
+  'prefix': 'QCRYPTO_RSA_PADDING_ALG',
+  'data': ['raw', 'pkcs1']}
+
+##
+# @QCryptoAkCipherOptionsRSA:
+#
+# Specific parameters for RSA algorithm.
+#
+# @hash-alg: QCryptoHashAlgorithm
+# @padding-alg: QCryptoRSAPaddingAlgorithm
+#
+# Since: 7.1
+##
+{ 'struct': 'QCryptoAkCipherOptionsRSA',
+  'data': { 'hash-alg':'QCryptoHashAlgorithm',
+            'padding-alg': 'QCryptoRSAPaddingAlgorithm'}}
+
+##
+# @QCryptoAkCipherOptions:
+#
+# The options that are available for all asymmetric key algorithms
+# when creating a new QCryptoAkCipher.
+#
+# Since: 7.1
+##
+{ 'union': 'QCryptoAkCipherOptions',
+  'base': { 'alg': 'QCryptoAkCipherAlgorithm' },
+  'discriminator': 'alg',
+  'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 3/9] crypto: Introduce akcipher crypto class
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
  2022-04-28 13:59 ` [PATCH v5 1/9] virtio-crypto: header update zhenwei pi
  2022-04-28 13:59 ` [PATCH v5 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-12 10:04   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 4/9] crypto: add ASN.1 DER decoder zhenwei pi
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

Introduce new akcipher crypto class 'QCryptoAkCIpher', which supports
basic asymmetric operations: encrypt, decrypt, sign and verify.

Suggested by Daniel P. Berrangé, also add autoptr cleanup for the new
class. Thanks to Daniel!

Co-developed-by: lei he <helei.sig11@bytedance.com>
Signed-off-by: lei he <helei.sig11@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
---
 crypto/akcipher.c         | 102 ++++++++++++++++++++++++
 crypto/akcipherpriv.h     |  55 +++++++++++++
 crypto/meson.build        |   1 +
 include/crypto/akcipher.h | 158 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 316 insertions(+)
 create mode 100644 crypto/akcipher.c
 create mode 100644 crypto/akcipherpriv.h
 create mode 100644 include/crypto/akcipher.h

diff --git a/crypto/akcipher.c b/crypto/akcipher.c
new file mode 100644
index 0000000000..ab28bf415b
--- /dev/null
+++ b/crypto/akcipher.c
@@ -0,0 +1,102 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi <pizhenwei@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/akcipher.h"
+#include "akcipherpriv.h"
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+                                      QCryptoAkCipherKeyType type,
+                                      const uint8_t *key, size_t keylen,
+                                      Error **errp)
+{
+    QCryptoAkCipher *akcipher = NULL;
+
+    return akcipher;
+}
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+    return false;
+}
+
+int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
+                             const void *in, size_t in_len,
+                             void *out, size_t out_len, Error **errp)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    return drv->encrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_decrypt(QCryptoAkCipher *akcipher,
+                             const void *in, size_t in_len,
+                             void *out, size_t out_len, Error **errp)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    return drv->decrypt(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_sign(QCryptoAkCipher *akcipher,
+                          const void *in, size_t in_len,
+                          void *out, size_t out_len, Error **errp)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    return drv->sign(akcipher, in, in_len, out, out_len, errp);
+}
+
+int qcrypto_akcipher_verify(QCryptoAkCipher *akcipher,
+                            const void *in, size_t in_len,
+                            const void *in2, size_t in2_len, Error **errp)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    return drv->verify(akcipher, in, in_len, in2, in2_len, errp);
+}
+
+int qcrypto_akcipher_max_plaintext_len(QCryptoAkCipher *akcipher)
+{
+    return akcipher->max_plaintext_len;
+}
+
+int qcrypto_akcipher_max_ciphertext_len(QCryptoAkCipher *akcipher)
+{
+    return akcipher->max_ciphertext_len;
+}
+
+int qcrypto_akcipher_max_signature_len(QCryptoAkCipher *akcipher)
+{
+    return akcipher->max_signature_len;
+}
+
+int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher)
+{
+    return akcipher->max_dgst_len;
+}
+
+void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    drv->free(akcipher);
+}
diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h
new file mode 100644
index 0000000000..739f639bcf
--- /dev/null
+++ b/crypto/akcipherpriv.h
@@ -0,0 +1,55 @@
+/*
+ * QEMU Crypto asymmetric algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi <pizhenwei@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QCRYPTO_AKCIPHERPRIV_H
+#define QCRYPTO_AKCIPHERPRIV_H
+
+#include "qapi/qapi-types-crypto.h"
+
+typedef struct QCryptoAkCipherDriver QCryptoAkCipherDriver;
+
+struct QCryptoAkCipher {
+    QCryptoAkCipherAlgorithm alg;
+    QCryptoAkCipherKeyType type;
+    int max_plaintext_len;
+    int max_ciphertext_len;
+    int max_signature_len;
+    int max_dgst_len;
+    QCryptoAkCipherDriver *driver;
+};
+
+struct QCryptoAkCipherDriver {
+    int (*encrypt)(QCryptoAkCipher *akcipher,
+                   const void *in, size_t in_len,
+                   void *out, size_t out_len, Error **errp);
+    int (*decrypt)(QCryptoAkCipher *akcipher,
+                   const void *out, size_t out_len,
+                   void *in, size_t in_len, Error **errp);
+    int (*sign)(QCryptoAkCipher *akcipher,
+                const void *in, size_t in_len,
+                void *out, size_t out_len, Error **errp);
+    int (*verify)(QCryptoAkCipher *akcipher,
+                  const void *in, size_t in_len,
+                  const void *in2, size_t in2_len, Error **errp);
+    void (*free)(QCryptoAkCipher *akcipher);
+};
+
+#endif /* QCRYPTO_AKCIPHER_H */
diff --git a/crypto/meson.build b/crypto/meson.build
index 19c44bea89..7647d5e243 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -1,6 +1,7 @@
 crypto_ss.add(genh)
 crypto_ss.add(files(
   'afsplit.c',
+  'akcipher.c',
   'block-luks.c',
   'block-qcow.c',
   'block.c',
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
new file mode 100644
index 0000000000..51f5fa2774
--- /dev/null
+++ b/include/crypto/akcipher.h
@@ -0,0 +1,158 @@
+/*
+ * QEMU Crypto asymmetric algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: zhenwei pi <pizhenwei@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QCRYPTO_AKCIPHER_H
+#define QCRYPTO_AKCIPHER_H
+
+#include "qapi/qapi-types-crypto.h"
+
+typedef struct QCryptoAkCipher QCryptoAkCipher;
+
+/**
+ * qcrypto_akcipher_supports:
+ * @opts: the asymmetric key algorithm and related options
+ *
+ * Determine if asymmetric key cipher decribed with @opts is
+ * supported by the current configured build
+ *
+ * Returns: true if it is supported, false otherwise.
+ */
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts);
+
+/**
+ * qcrypto_akcipher_new:
+ * @opts: specify the algorithm and the related arguments
+ * @type: private or public key type
+ * @key: buffer to store the key
+ * @key_len: the length of key buffer
+ * @errp: error pointer
+ *
+ * Create akcipher context
+ *
+ * Returns: On success, a new QCryptoAkCipher initialized with @opt
+ * is created and returned, otherwise NULL is returned.
+ */
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+                                      QCryptoAkCipherKeyType type,
+                                      const uint8_t *key, size_t key_len,
+                                      Error **errp);
+
+/**
+ * qcrypto_akcipher_encrypt:
+ * @akcipher: akcipher context
+ * @in: plaintext pending to be encrypted
+ * @in_len: length of plaintext, less or equal to the size reported
+ *          by a call to qcrypto_akcipher_max_plaintext_len()
+ * @out: buffer to store the ciphertext
+ * @out_len: length of ciphertext, less or equal to the size reported
+ *           by a call to qcrypto_akcipher_max_ciphertext_len()
+ * @errp: error pointer
+ *
+ * Encrypt @in and write ciphertext into @out
+ *
+ * Returns: length of ciphertext if encrypt succeed,
+ *          otherwise -1 is returned
+ */
+int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
+                             const void *in, size_t in_len,
+                             void *out, size_t out_len, Error **errp);
+
+/**
+ * qcrypto_akcipher_decrypt:
+ * @akcipher: akcipher context
+ * @in: ciphertext to be decrypted
+ * @in_len: the length of ciphertext, less or equal to the size reported
+ *          by a call to qcrypto_akcipher_max_ciphertext_len()
+ * @out: buffer to store the plaintext
+ * @out_len: length of the plaintext buffer, less or equal to the size
+ *           reported by a call to qcrypto_akcipher_max_plaintext_len()
+ * @errp: error pointer
+ *
+ * Decrypt @in and write plaintext into @out
+ *
+ * Returns: length of plaintext if decrypt succeed,
+ *          otherwise -1 is returned
+ */
+int qcrypto_akcipher_decrypt(QCryptoAkCipher *akcipher,
+                             const void *in, size_t in_len,
+                             void *out, size_t out_len, Error **errp);
+
+/**
+ * qcrypto_akcipher_sign:
+ * @akcipher: akcipher context
+ * @in: data to be signed
+ * @in_len: the length of data, less or equal to the size reported
+ *          by a call to qcrypto_akcipher_max_dgst_len()
+ * @out: buffer to store the signature
+ * @out_len: length of the signature buffer, less or equal to the size
+ *           by a call to qcrypto_akcipher_max_signature_len()
+ * @errp: error pointer
+ *
+ * Generate signature for @in, write into @out
+ *
+ * Returns: length of signature if succeed,
+ *          otherwise -1 is returned
+ */
+int qcrypto_akcipher_sign(QCryptoAkCipher *akcipher,
+                          const void *in, size_t in_len,
+                          void *out, size_t out_len, Error **errp);
+
+/**
+ * qcrypto_akcipher_verify:
+ * @akcipher: akcipher context
+ * @in: pointer to the signature
+ * @in_len: length of signature, ess or equal to the size reported
+ *          by a call to qcrypto_akcipher_max_signature_len()
+ * @in2: pointer to original data
+ * @in2_len: the length of original data, less or equal to the size
+ *           by a call to qcrypto_akcipher_max_dgst_len()
+ * @errp: error pointer
+ *
+ * Verify @in and @in2 match or not
+ *
+ * Returns: 0 for succeed,
+ *          otherwise -1 is returned
+ */
+int qcrypto_akcipher_verify(QCryptoAkCipher *akcipher,
+                            const void *in, size_t in_len,
+                            const void *in2, size_t in2_len, Error **errp);
+
+int qcrypto_akcipher_max_plaintext_len(QCryptoAkCipher *akcipher);
+
+int qcrypto_akcipher_max_ciphertext_len(QCryptoAkCipher *akcipher);
+
+int qcrypto_akcipher_max_signature_len(QCryptoAkCipher *akcipher);
+
+int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher);
+
+/**
+ * qcrypto_akcipher_free:
+ * @akcipher: akcipher context
+ *
+ * Free the akcipher context
+ *
+ */
+void qcrypto_akcipher_free(QCryptoAkCipher *akcipher);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipher, qcrypto_akcipher_free)
+
+#endif /* QCRYPTO_AKCIPHER_H */
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 4/9] crypto: add ASN.1 DER decoder
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (2 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 3/9] crypto: Introduce akcipher crypto class zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-12  9:46   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed zhenwei pi
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

From: Lei He <helei.sig11@bytedance.com>

Add an ANS.1 DER decoder which is used to parse asymmetric
cipher keys

Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 crypto/der.c                 | 190 +++++++++++++++++++++++
 crypto/der.h                 |  82 ++++++++++
 crypto/meson.build           |   1 +
 tests/unit/meson.build       |   1 +
 tests/unit/test-crypto-der.c | 290 +++++++++++++++++++++++++++++++++++
 5 files changed, 564 insertions(+)
 create mode 100644 crypto/der.c
 create mode 100644 crypto/der.h
 create mode 100644 tests/unit/test-crypto-der.c

diff --git a/crypto/der.c b/crypto/der.c
new file mode 100644
index 0000000000..7907bcfd51
--- /dev/null
+++ b/crypto/der.c
@@ -0,0 +1,190 @@
+/*
+ * QEMU Crypto ASN.1 DER decoder
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "crypto/der.h"
+
+enum QCryptoDERTypeTag {
+    QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
+    QCRYPTO_DER_TYPE_TAG_INT = 0x2,
+    QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
+    QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
+    QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
+    QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
+    QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
+    QCRYPTO_DER_TYPE_TAG_SET = 0x11,
+};
+
+#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
+#define QCRYPTO_DER_SHORT_LEN_MASK 0x80
+
+static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
+{
+    return **data;
+}
+
+static void qcrypto_der_cut_nbytes(const uint8_t **data,
+                                   size_t *dlen,
+                                   size_t nbytes)
+{
+    *data += nbytes;
+    *dlen -= nbytes;
+}
+
+static uint8_t qcrypto_der_cut_byte(const uint8_t **data, size_t *dlen)
+{
+    uint8_t val = qcrypto_der_peek_byte(data, dlen);
+
+    qcrypto_der_cut_nbytes(data, dlen, 1);
+
+    return val;
+}
+
+static int qcrypto_der_invoke_callback(DERDecodeCb cb, void *ctx,
+                                       const uint8_t *value, size_t vlen,
+                                       Error **errp)
+{
+    if (!cb) {
+        return 0;
+    }
+
+    return cb(ctx, value, vlen, errp);
+}
+
+static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t *dlen,
+                                             DERDecodeCb cb, void *ctx,
+                                             Error **errp)
+{
+    const uint8_t *value;
+    size_t vlen = 0;
+    uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);
+
+    /* short format of definite-length */
+    if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
+        if (byte_count > *dlen) {
+            error_setg(errp, "Invalid content length: %u", byte_count);
+            return -1;
+        }
+
+        value = *data;
+        vlen = byte_count;
+        qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+        if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+            return -1;
+        }
+        return vlen;
+    }
+
+    /* Ignore highest bit */
+    byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;
+
+    /*
+     * size_t is enough to store the value of length, although the DER
+     * encoding standard supports larger length.
+     */
+    if (byte_count > sizeof(size_t)) {
+        error_setg(errp, "Invalid byte count of content length: %u",
+                   byte_count);
+        return -1;
+    }
+
+    if (*dlen < byte_count) {
+        error_setg(errp, "Invalid content length: %u", byte_count);
+        return -1;
+    }
+    while (byte_count--) {
+        vlen <<= 8;
+        vlen += qcrypto_der_cut_byte(data, dlen);
+    }
+
+    if (vlen > *dlen) {
+        error_setg(errp, "Invalid content length: %lu", vlen);
+        return -1;
+    }
+
+    value = *data;
+    qcrypto_der_cut_nbytes(data, dlen, vlen);
+
+    if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
+        return -1;
+    }
+    return vlen;
+}
+
+static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
+                                    DERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t val;
+    if (*dlen < 1) {
+        error_setg(errp, "Need more data");
+        return -1;
+    }
+    val = qcrypto_der_peek_byte(data, dlen);
+
+    /* must use definite length format */
+    if (val == QCRYPTO_DER_SHORT_LEN_MASK) {
+        error_setg(errp, "Only definite length format is allowed");
+        return -1;
+    }
+
+    return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
+                           DERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag;
+    if (*dlen < 1) {
+        error_setg(errp, "Need more data");
+        return -1;
+    }
+    tag = qcrypto_der_cut_byte(data, dlen);
+
+    /* INTEGER must encoded in primitive-form */
+    if (tag != QCRYPTO_DER_TYPE_TAG_INT) {
+        error_setg(errp, "Invalid integer type tag: %u", tag);
+        return -1;
+    }
+
+    return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen,
+                           DERDecodeCb cb, void *ctx, Error **errp)
+{
+    uint8_t tag;
+    if (*dlen < 1) {
+        error_setg(errp, "Need more data");
+        return -1;
+    }
+    tag = qcrypto_der_cut_byte(data, dlen);
+
+    /* SEQUENCE must use constructed form */
+    if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) {
+        error_setg(errp, "Invalid type sequence tag: %u", tag);
+        return -1;
+    }
+
+    return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+}
diff --git a/crypto/der.h b/crypto/der.h
new file mode 100644
index 0000000000..aaa0e01969
--- /dev/null
+++ b/crypto/der.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QCRYPTO_ASN1_DECODER_H
+#define QCRYPTO_ASN1_DECODER_H
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+
+/* Simple decoder used to parse DER encoded rsa keys. */
+
+/**
+ *  @opaque: user context.
+ *  @value: the starting address of |value| part of 'Tag-Length-Value' pattern.
+ *  @vlen: length of the |value|.
+ *  Returns: 0 for success, any other value is considered an error.
+ */
+typedef int (*DERDecodeCb) (void *opaque, const uint8_t *value,
+                            size_t vlen, Error **errp);
+
+/**
+ * der_decode_int:
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Decode integer from DER-encoded data.
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded INTEGER will be returned. Otherwise, -1 is
+ * returned.
+ */
+int qcrypto_der_decode_int(const uint8_t **data,
+                           size_t *dlen,
+                           DERDecodeCb cb,
+                           void *opaque,
+                           Error **errp);
+
+/**
+ * der_decode_seq:
+ *
+ * Decode sequence from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded SEQUENCE will be returned. Otherwise, -1 is
+ * returned.
+ */
+int qcrypto_der_decode_seq(const uint8_t **data,
+                           size_t *dlen,
+                           DERDecodeCb cb,
+                           void *opaque,
+                           Error **errp);
+
+#endif  /* QCRYPTO_ASN1_DECODER_H */
diff --git a/crypto/meson.build b/crypto/meson.build
index 7647d5e243..c9b36857a6 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -6,6 +6,7 @@ crypto_ss.add(files(
   'block-qcow.c',
   'block.c',
   'cipher.c',
+  'der.c',
   'hash.c',
   'hmac.c',
   'ivgen-essiv.c',
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index ab01e00f12..a82603d7cb 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -47,6 +47,7 @@ tests = {
   'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'],
   'test-qapi-util': [],
   'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'],
+  'test-crypto-der': [crypto],
 }
 
 if have_system or have_tools
diff --git a/tests/unit/test-crypto-der.c b/tests/unit/test-crypto-der.c
new file mode 100644
index 0000000000..4ce578284f
--- /dev/null
+++ b/tests/unit/test-crypto-der.c
@@ -0,0 +1,290 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/der.h"
+
+/* rsa(512) private key, generated by openssl */
+static const uint8_t test_rsa512_priv_key[] =
+    "\x30\x82\x01\x39"      /* SEQUENCE, offset: 0, length: 313 */
+    "\x02\x01\x00"          /* INTEGER, offset: 4, length: 1 */
+    "\x02\x41"              /* INTEGER, offset: 7, length: 65 */
+    "\x00\xb9\xe1\x22\xdb\x56\x2f\xb6\xf7\xf0\x0a\x87\x43\x07\x12\xdb"
+    "\x6d\xb6\x2b\x41\x8d\x2c\x3c\xa5\xdd\x78\x9a\x8f\xab\x8e\xf2\x4a"
+    "\xc8\x34\x0c\x12\x4f\x11\x90\xc6\xc2\xa5\xd0\xcd\xfb\xfc\x2c\x95"
+    "\x56\x82\xdf\x39\xf3\x3b\x1d\x62\x26\x97\xb7\x93\x25\xc7\xec\x7e"
+    "\xf7"
+    "\x02\x03\x01\x00\x01"  /* INTEGER, offset: 74, length: 3 */
+    "\x02\x40"              /* INTEGER, offset: 79, length: 64 */
+    "\x1e\x80\xfe\xda\x65\xdb\x70\xb8\x61\x91\x28\xbf\x6c\x32\xc1\x05"
+    "\xd1\x26\x6a\x1c\x83\xcc\xf4\x1f\x53\x42\x72\x1f\x62\x57\x0a\xc4"
+    "\x66\x76\x30\x87\xb9\xb1\xb9\x6a\x63\xfd\x8f\x3e\xfc\x35\x3f\xd6"
+    "\x2e\x6c\xc8\x70\x8a\x17\xc1\x28\x6a\xfe\x51\x56\xb3\x92\x6f\x09"
+    "\x02\x21"              /* INTEGER, offset: 145, length: 33 */
+    "\x00\xe3\x2e\x2d\x8d\xba\x1c\x34\x4c\x49\x9f\xc1\xa6\xdd\xd7\x13"
+    "\x8d\x05\x48\xdd\xff\x5c\x30\xbc\x6b\xc4\x18\x9d\xfc\xa2\xd0\x9b"
+    "\x4d"
+    "\x02\x21"             /* INTEGER, offset: 180, length: 33 */
+    "\x00\xd1\x75\xaf\x4b\xc6\x1a\xb0\x98\x14\x42\xae\x33\xf3\x44\xde"
+    "\x21\xcb\x04\xda\xfb\x1e\x35\x92\xcd\x69\xc0\x83\x06\x83\x8e\x39"
+    "\x53"
+    "\x02\x20"             /* INTEGER, offset: 215, length: 32 */
+    "\x68\x8d\x2a\xf7\xcb\xcc\x09\x21\x86\xcc\x98\x21\xc4\x7c\xa4\x09"
+    "\xc5\x81\xd8\x71\x1a\x2b\x6f\xbb\xa4\xde\xb3\x6e\xbe\x3b\x85\x0d"
+    "\x02\x20"             /* INTEGER, offset: 249, length: 32 */
+    "\x64\x06\x0e\xef\xe0\x6a\x5e\x6a\x41\x42\x96\x6d\xb8\x7d\xea\x95"
+    "\xb8\x9d\x58\xf5\x12\x38\x03\x22\x94\x9d\x99\xf4\x42\x5e\x68\x81"
+    "\x02\x20"             /* INTEGER, offset: 283, length: 32 */
+    "\x7f\x1d\x87\xe8\x55\x30\x75\xc7\x29\xec\xc9\x65\x76\x5a\x6a\xa3"
+    "\x4a\x6e\xe1\x26\x65\xd1\x76\xd5\xb9\xd1\x8b\xa8\x73\xe2\x6a\x9e";
+
+static const uint8_t test_rsa2048_priv_key[] =
+    "\x30\x82\x04\xa6"          /* SEQUENCE, offset: 0, length 1190 */
+    "\x02\x01\x00"              /* INTEGER, offset: 4, length: 1 */
+    "\x02\x82\x01\x01"          /* INTEGER, offset: 7, length: 257 */
+    "\x00\xd1\x48\xc2\xc1\x1d\x4f\x94\xf2\xbb\x9b\xe2\x2d\xe1\xea\x4c"
+    "\xce\x41\x72\xe3\x41\x7e\x9d\x91\x85\xa3\x4e\xe1\x2c\xf6\x52\x6d"
+    "\xf9\x84\x64\xdf\x87\x28\x4a\xc9\x9d\x78\x93\x47\xc8\xd9\x66\x2e"
+    "\xf4\xc6\xf0\x32\x15\x1a\xe8\xaf\x5a\xca\x3a\xd3\x3e\xf6\xde\x86"
+    "\xdd\x9b\xa6\x4d\x74\x58\xf0\x11\x7f\x66\xd5\x1c\xd8\xde\xa3\xf8"
+    "\xa3\xfc\x33\x55\x89\xa9\xc3\xea\x5b\x2e\x31\x06\xf8\xcb\x9e\x6e"
+    "\xb2\x68\x0d\xe6\xc3\x5c\x2d\xf8\xa2\xbd\x00\x1a\xf6\xb6\xdd\x14"
+    "\x8d\x11\x6d\x2d\xc6\x0c\x09\xe6\xf6\xb9\x8b\x87\x4c\x9f\x4d\x63"
+    "\xd3\x94\xf4\x32\xca\xcf\x5e\xbf\xe2\x7f\x73\x5a\x65\xec\x82\x0d"
+    "\x7f\x30\x25\x03\xd4\x3a\xff\xa2\xe8\xd6\xb5\x1f\x4f\x36\x64\x61"
+    "\xc3\x5f\xb2\x9e\x0c\x53\x04\x19\x34\x99\xe8\xe3\xe6\xd3\x2f\x45"
+    "\x58\x8e\x5d\x54\x5a\xa0\xc0\x5e\x51\x9b\x22\x15\xec\x26\x6f\x72"
+    "\x68\xe9\xbf\x5d\x1d\xb5\xd9\xe4\x81\x1a\x92\x66\xa8\xcb\x73\x46"
+    "\xab\x96\x7b\xf8\x9c\xf5\xb5\x9e\x2b\x13\x71\xe0\x01\x0c\x59\x1b"
+    "\x63\x9f\xb7\xd1\xcd\x47\x8e\xc7\x3a\xbe\xcb\x47\xa7\x23\x43\xa7"
+    "\x7d\xbd\x2c\x4e\x22\x37\xcc\xf9\x1b\x1b\xbb\xed\xec\xf0\x47\x92"
+    "\x43"
+    "\x02\x03\x01\x00\x01"      /* INTEGER, offset 268, length 3 */
+    "\x02\x82\x01\x01"          /* INTEGER, offset 273, length 257 */
+    "\x00\x8d\x21\x97\x0c\x29\x9a\xf8\x23\xf4\x76\x3b\xc1\x9b\x3e\xa8"
+    "\x8a\xd2\xc2\x0a\x14\xa9\xb0\xd2\x68\x9f\x67\x5b\x1c\x3a\x03\xfe"
+    "\x5b\xac\x77\x65\xf1\xbc\x2f\x2a\xe5\x01\x61\xb8\x9f\xee\x53\x25"
+    "\x49\x36\x3a\xd6\x5b\x3b\x29\x3c\xcf\x69\xde\xdf\x83\xef\x70\xc2"
+    "\xdc\x00\xd1\xd6\x1b\xa6\xba\x45\xe2\x77\x53\x31\xbf\xe1\xec\x0b"
+    "\x89\x72\x52\x9f\xd5\x54\xe1\x64\x52\x16\xc5\x43\x21\x56\x16\xc2"
+    "\x29\x97\x58\x00\x8d\x2f\xc5\x64\x8d\x42\x0d\x27\x21\xc6\xd1\x31"
+    "\xc1\xab\xc5\xc7\x7f\x6d\xb0\xe3\xca\xef\xf6\xf2\xc7\xae\x09\xbf"
+    "\x4d\xc0\x4e\x90\x2c\x28\xb9\xcc\x22\x74\xf2\xd5\xff\x4d\x86\xf6"
+    "\xec\x45\x1f\xbf\x25\x4c\x30\x26\x76\x4f\x09\x13\x83\xef\x35\x73"
+    "\xa3\xa2\xb1\x40\xcf\x07\x7a\x83\xae\xea\x00\xea\x74\xc7\x54\x6a"
+    "\x88\x19\xed\x35\xd3\x7e\x5e\xac\x51\xc1\x1e\x5e\x2c\x57\x72\x20"
+    "\x10\x6a\x0c\x47\xe1\xf0\x36\x70\xd2\xa7\x57\x64\x47\x46\x9f\xca"
+    "\x23\x8a\x48\x50\x1d\x33\x6a\x86\x46\x69\xed\x54\x65\x6b\x9e\xab"
+    "\x1f\x84\x87\xf4\x92\x8a\x6c\x44\x20\xaa\x8d\xd8\x50\xde\x45\x74"
+    "\xe0\xa8\xc7\xb9\x38\x74\x24\x51\x33\xf0\x39\x54\x6c\x11\xae\xc2"
+    "\x29"
+    "\x02\x81\x81"              /* INTEGER, offset 534, length 129 */
+    "\x00\xe8\x26\xd1\xf9\xa0\xd3\x0e\x3f\x2f\x89\x9b\x94\x16\x12\xd1"
+    "\xae\x3c\x53\x9c\xcf\xc6\xf7\x03\xf5\xdf\x39\xdc\x25\x5d\xcb\xb8"
+    "\xb9\x74\x3e\x3b\x36\xf6\xa0\x8d\xb1\x0e\xd8\xfe\x8c\xcd\x01\x13"
+    "\x77\x73\x08\x0f\x32\xbd\xe6\x95\xdc\xd0\x14\x7d\x44\xdc\x3e\xd9"
+    "\xaa\x8a\x32\xe6\x0e\x76\xb6\x05\xc5\x6b\x87\x78\x9a\x32\xe2\xf8"
+    "\x78\xba\x58\x75\x58\xd5\x26\x9d\x9a\x0f\xb6\xca\xb5\x27\xd8\x58"
+    "\xae\x3f\x49\x54\xd2\x2b\xac\x28\x39\x88\x31\x42\x12\x08\xea\x0b"
+    "\x39\x58\xae\xf3\x82\xa0\xe2\x75\x7c\x96\xa9\xb8\x57\x29\x6d\xd7"
+    "\x37"
+    "\x02\x81\x81"              /* INTEGER, offset 666, length 129 */
+    "\x00\xe6\xc8\x91\x50\x49\x97\x56\x70\x6e\x25\xf5\x77\x25\xa5\x41"
+    "\xfe\xd7\x25\x1b\xc1\x4a\xff\x37\x44\x2b\x46\xa0\xdf\xe8\x02\x09"
+    "\xdd\xa8\x41\xa1\x12\x84\x3c\xf8\xc2\x13\x3e\xb8\x4b\x22\x01\xac"
+    "\xa6\x09\xb2\xe9\xcd\xc8\x51\xee\xde\xa3\x1e\x6b\xfe\xb1\xf8\xb6"
+    "\x9e\x48\x36\x62\x0b\x05\xfa\x38\xc1\x06\x04\x58\x95\x4d\x25\x13"
+    "\x6d\x0b\x12\x0b\xc9\x6d\x59\xfc\x33\x03\x36\x01\x12\x09\x72\x74"
+    "\x5e\x98\x65\x66\x2f\x3a\xde\xd8\xd4\xee\x6f\x82\xe6\x36\x49\x12"
+    "\x6a\x94\x28\xe9\x28\x9e\xef\x29\xdc\xdf\xab\x94\x65\x02\x4e\x4b"
+    "\x55"
+    "\x02\x81\x81"              /* INTEGER, offset 798, length 129 */
+    "\x00\xc9\xda\xb7\x48\x6e\x66\x15\x45\x2b\x78\x63\x26\x67\xeb\x05"
+    "\x16\x92\xad\xc0\xf3\x88\xf4\xcf\x24\xc2\x6b\xf4\xd7\x28\xaf\x32"
+    "\x77\x4e\x73\xad\xd9\x24\xa8\x85\x8b\x26\x75\xd7\x1f\x66\x41\x41"
+    "\x43\xe3\x69\x66\x8d\xa0\x41\x16\x9d\x60\xef\xef\xdc\x28\x05\x1e"
+    "\x0e\x03\x0c\x2e\xac\xf4\xdb\x60\x39\x40\x3e\x12\xc7\x40\xe7\xc9"
+    "\x54\x6f\xf2\xea\x55\xcb\x40\x40\x58\xec\xc0\xeb\x90\x88\x8c\xbc"
+    "\xcf\x05\x88\x25\x90\x79\x18\xc0\x01\x06\x42\x8e\x48\x50\x27\xf0"
+    "\x8a\x74\x69\xea\xa1\xf2\x71\xf5\xe5\xd6\xba\xcb\xe6\x3d\xc7\x9c"
+    "\x11"
+     "\x02\x81\x81"              /* INTEGER, offset 930, length 129 */
+    "\x00\xc9\xf5\x04\xad\x34\xe9\x39\xdc\x83\x97\xb6\x3a\x40\xf8\x60"
+    "\x4b\x69\xec\xf0\x5f\xf3\x88\x69\xcd\xbe\xed\x3c\xc5\x14\x5c\x0c"
+    "\x54\x2b\xf4\xda\xc6\xc0\x70\x36\xe4\x67\x41\x00\xb7\xc7\x17\x9e"
+    "\x05\x63\x01\x6d\x77\x06\x71\x24\xcf\x32\x01\xe2\x51\xed\x5e\x90"
+    "\x38\xed\x4a\xa1\xfb\xb1\x8c\x69\xf4\x08\x96\xef\x0a\x20\x8b\x6c"
+    "\x77\x85\x33\x92\x9a\xff\x95\xba\x8c\xcd\xa7\x89\xc2\x46\x00\x21"
+    "\xf3\xd1\xfb\x12\x34\x0c\x99\x8d\x38\xb1\x3b\x66\x5a\x9d\x70\xce"
+    "\xab\xf3\xe1\xe5\x40\x05\xed\x97\x3d\xd1\x82\x6e\x07\x02\xc0\x8f"
+    "\x4d"
+    "\x02\x81\x81"              /* INTEGER, offset 1062, length 129 */
+    "\x00\xe4\x96\x79\xa8\x6a\x70\xdd\x67\x42\xff\x15\x11\x9e\x01\x71"
+    "\xac\xf1\x70\x7d\x87\xe2\x6e\x0c\x4d\xbb\x21\x15\xbb\xa7\x4e\x0c"
+    "\x09\x7e\x82\xca\x91\xbe\xd0\xdd\x9c\x8c\xb0\x77\x64\x30\x1b\x7e"
+    "\xbb\x69\xcb\x4c\xde\xd6\x6a\xb9\x72\x15\x79\xdc\x05\x99\x69\x8b"
+    "\x24\xa1\xad\x13\x35\x31\xc0\x0b\xf1\xd2\x06\x7c\x94\x1a\x21\x2f"
+    "\x02\xb9\xf0\xd0\xbb\xf7\xb7\x78\xf9\x3d\x76\x60\xd6\x6b\x5f\x35"
+    "\x88\x14\x33\xe6\xbc\xca\x6b\x88\x90\x57\x3b\x0c\xa3\x6e\x47\xdf"
+    "\x4e\x2f\x4c\xf9\xab\x97\x38\xe4\x20\x32\x32\x96\xc8\x9e\x79\xd3"
+    "\x12";
+
+#define MAX_CHECKER_COUNT 32
+
+typedef struct QCryptoAns1DecoderResultChecker QCryptoAns1DecoderResultChecker;
+struct QCryptoAns1DecoderResultChecker {
+    int (*action) (const uint8_t **data, size_t *dlen,
+                   DERDecodeCb cb, void *opaque, Error **errp);
+    DERDecodeCb cb;
+    const uint8_t *exp_value;
+    size_t exp_vlen;
+};
+
+typedef struct QCryptoAns1DecoderTestData QCryptoAns1DecoderTestData;
+struct QCryptoAns1DecoderTestData {
+    const char *path;
+    const uint8_t *test_data;
+    size_t test_data_len;
+    QCryptoAns1DecoderResultChecker checker[MAX_CHECKER_COUNT];
+};
+
+typedef struct QCryptoAns1DecoderTestContext QCryptoAns1DecoderTestContext;
+struct QCryptoAns1DecoderTestContext {
+    const uint8_t *data;
+    size_t dlen;
+};
+
+static int checker_callback(void *opaque, const uint8_t *value,
+                            size_t vlen, Error **errp)
+{
+    QCryptoAns1DecoderResultChecker *checker =
+        (QCryptoAns1DecoderResultChecker *)opaque;
+
+    g_assert(value == checker->exp_value);
+    g_assert(vlen == checker->exp_vlen);
+    return 0;
+}
+
+static void test_ans1(const void *opaque)
+{
+    const QCryptoAns1DecoderTestData *test_data =
+        (QCryptoAns1DecoderTestData *)opaque;
+    QCryptoAns1DecoderTestContext ctx[MAX_CHECKER_COUNT];
+    int seq_depth = 0, checker_idx = 0;
+    ctx[seq_depth].data = test_data->test_data;
+    ctx[seq_depth].dlen = test_data->test_data_len;
+    bool all_checker_completed = false;
+
+    do {
+        const QCryptoAns1DecoderResultChecker *checker =
+            &test_data->checker[checker_idx++];
+        QCryptoAns1DecoderTestContext *c = &ctx[seq_depth];
+        if (!checker->action) {
+            all_checker_completed = true;
+            break;
+        }
+        g_assert(checker->action(&c->data, &c->dlen, checker_callback,
+                                 (void *)checker, &error_abort)
+            == checker->exp_vlen);
+        if (checker->action == qcrypto_der_decode_seq) {
+            ++seq_depth;
+            ctx[seq_depth].data = checker->exp_value;
+            ctx[seq_depth].dlen = checker->exp_vlen;
+        }
+        while (seq_depth != 0 && ctx[seq_depth].dlen == 0) {
+            --seq_depth;
+        }
+
+    } while (true);
+    g_assert(seq_depth == 0);
+    g_assert(ctx[seq_depth].dlen == 0);
+    g_assert(all_checker_completed);
+}
+
+static QCryptoAns1DecoderTestData test_data[] = {
+{
+    .path = "/crypto/der/parse-rsa512-priv-key",
+    .test_data = test_rsa512_priv_key,
+    .test_data_len = sizeof(test_rsa512_priv_key) - 1,
+    .checker = {
+        { qcrypto_der_decode_seq, checker_callback,
+          test_rsa512_priv_key + 4, 313 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 4 + 2, 1 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 7 + 2, 65 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 74 + 2, 3 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 79 + 2, 64 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 145 + 2, 33 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 180 + 2, 33 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 215 + 2, 32 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 249 + 2, 32 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa512_priv_key + 283 + 2, 32 },
+    },
+},
+{
+    .path = "/crypto/der/parse-rsa2048-priv-key",
+    .test_data = test_rsa2048_priv_key,
+    .test_data_len = sizeof(test_rsa2048_priv_key) - 1,
+    .checker = {
+        { qcrypto_der_decode_seq, checker_callback,
+          test_rsa2048_priv_key + 4, 1190 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 4 + 2, 1 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 7 + 4, 257 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 268 + 2, 3 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 273 + 4, 257 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 534 + 3, 129 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 666 + 3, 129 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 798 + 3, 129 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 930 + 3, 129 },
+        { qcrypto_der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 1062 + 3, 129 },
+    },
+},
+
+};
+
+int main(int argc, char **argv)
+{
+    size_t i;
+    g_test_init(&argc, &argv, NULL);
+
+    for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
+        g_test_add_data_func(test_data[i].path, &test_data[i], test_ans1);
+    }
+
+    return g_test_run();
+}
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (3 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 4/9] crypto: add ASN.1 DER decoder zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-13 10:55   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt zhenwei pi
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

From: Lei He <helei.sig11@bytedance.com>

Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
a 'real' RSA backend to handle request from guest side. It's
important to test RSA offload case without OS & hardware requirement.

Signed-off-by: lei he <helei.sig11@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
---
 crypto/akcipher-nettle.c.inc | 432 +++++++++++++++++++++++++++++++++++
 crypto/akcipher.c            |   4 +
 crypto/meson.build           |   4 +
 crypto/rsakey-builtin.c.inc  | 209 +++++++++++++++++
 crypto/rsakey-nettle.c.inc   | 154 +++++++++++++
 crypto/rsakey.c              |  44 ++++
 crypto/rsakey.h              |  94 ++++++++
 meson.build                  |  11 +
 8 files changed, 952 insertions(+)
 create mode 100644 crypto/akcipher-nettle.c.inc
 create mode 100644 crypto/rsakey-builtin.c.inc
 create mode 100644 crypto/rsakey-nettle.c.inc
 create mode 100644 crypto/rsakey.c
 create mode 100644 crypto/rsakey.h

diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
new file mode 100644
index 0000000000..1f688aa0f1
--- /dev/null
+++ b/crypto/akcipher-nettle.c.inc
@@ -0,0 +1,432 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <nettle/rsa.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoNettleRSA {
+    QCryptoAkCipher akcipher;
+    struct rsa_public_key pub;
+    struct rsa_private_key priv;
+    QCryptoRSAPaddingAlgorithm padding_alg;
+    QCryptoHashAlgorithm hash_alg;
+} QCryptoNettleRSA;
+
+static void qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher)
+{
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+    if (!rsa) {
+        return;
+    }
+
+    rsa_public_key_clear(&rsa->pub);
+    rsa_private_key_clear(&rsa->priv);
+    g_free(rsa);
+}
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+    const QCryptoAkCipherOptionsRSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key,  size_t keylen,
+    Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+                                      QCryptoAkCipherKeyType type,
+                                      const uint8_t *key, size_t keylen,
+                                      Error **errp)
+{
+    switch (opts->alg) {
+    case QCRYPTO_AKCIPHER_ALG_RSA:
+        return qcrypto_nettle_rsa_new(&opts->u.rsa, type, key, keylen, errp);
+
+    default:
+        error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+        return NULL;
+    }
+
+    return NULL;
+}
+
+static void qcrypto_nettle_rsa_set_akcipher_size(QCryptoAkCipher *akcipher,
+                                                 int key_size)
+{
+    akcipher->max_plaintext_len = key_size;
+    akcipher->max_ciphertext_len = key_size;
+    akcipher->max_signature_len = key_size;
+    akcipher->max_dgst_len = key_size;
+}
+
+static int qcrypt_nettle_parse_rsa_private_key(QCryptoNettleRSA *rsa,
+                                               const uint8_t *key,
+                                               size_t keylen)
+{
+    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+        QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen);
+
+    if (!rsa_key) {
+        return -1;
+    }
+
+    nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+    nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.d, rsa_key->d.len, rsa_key->d.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.p, rsa_key->p.len, rsa_key->p.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.q, rsa_key->q.len, rsa_key->q.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.a, rsa_key->dp.len,
+                                  rsa_key->dp.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.b, rsa_key->dq.len,
+                                  rsa_key->dq.data);
+    nettle_mpz_init_set_str_256_u(rsa->priv.c, rsa_key->u.len, rsa_key->u.data);
+
+    if (!rsa_public_key_prepare(&rsa->pub)) {
+        return -1;
+    }
+
+    /**
+     * Since in the kernel's unit test, the p, q, a, b, c of some
+     * private keys is 0, only the simplest length check is done here
+     */
+    if (rsa_key->p.len > 1 &&
+        rsa_key->q.len > 1 &&
+        rsa_key->dp.len > 1 &&
+        rsa_key->dq.len > 1 &&
+        rsa_key->u.len > 1) {
+        if (!rsa_private_key_prepare(&rsa->priv)) {
+            return -1;
+        }
+    } else {
+        rsa->priv.size = rsa->pub.size;
+    }
+    qcrypto_nettle_rsa_set_akcipher_size(
+        (QCryptoAkCipher *)rsa, rsa->priv.size);
+
+    return 0;
+}
+
+static int qcrypt_nettle_parse_rsa_public_key(QCryptoNettleRSA *rsa,
+                                              const uint8_t *key,
+                                              size_t keylen)
+{
+    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+        QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen);
+
+    if (!rsa_key) {
+        return -1;
+    }
+    nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+    nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+
+    if (!rsa_public_key_prepare(&rsa->pub)) {
+        return -1;
+    }
+    qcrypto_nettle_rsa_set_akcipher_size(
+        (QCryptoAkCipher *)rsa, rsa->pub.size);
+
+    return 0;
+}
+
+static void wrap_nettle_random_func(void *ctx, size_t len, uint8_t *out)
+{
+    /* TODO: check result */
+    qcrypto_random_bytes(out, len, NULL);
+}
+
+static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher,
+                                      const void *data, size_t data_len,
+                                      void *enc, size_t enc_len,
+                                      Error **errp)
+{
+
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+    mpz_t c;
+    int ret = -1;
+
+    if (data_len > rsa->pub.size || enc_len != rsa->pub.size) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    /* Nettle do not support RSA encryption without any padding */
+    switch (rsa->padding_alg) {
+    case QCRYPTO_RSA_PADDING_ALG_RAW:
+        error_setg(errp, "RSA with raw padding is not supported");
+        break;
+
+    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
+        mpz_init(c);
+        if (rsa_encrypt(&rsa->pub, NULL, wrap_nettle_random_func,
+                          data_len, (uint8_t *)data, c) != 1) {
+            error_setg(errp, "Failed to encrypt");
+        } else {
+            nettle_mpz_get_str_256(enc_len, (uint8_t *)enc, c);
+            ret = enc_len;
+        }
+        mpz_clear(c);
+        break;
+
+    default:
+        error_setg(errp, "Unknown padding");
+    }
+
+    return ret;
+}
+
+static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,
+                                      const void *enc, size_t enc_len,
+                                      void *data, size_t data_len,
+                                      Error **errp)
+{
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+    mpz_t c;
+    int ret = -1;
+    if (enc_len > rsa->priv.size) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    switch (rsa->padding_alg) {
+    case QCRYPTO_RSA_PADDING_ALG_RAW:
+        error_setg(errp, "RSA with raw padding is not supported");
+        break;
+
+    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
+        nettle_mpz_init_set_str_256_u(c, enc_len, enc);
+        if (!rsa_decrypt(&rsa->priv, &data_len, (uint8_t *)data, c)) {
+            error_setg(errp, "Failed to decrypt");
+        } else {
+            ret = data_len;
+        }
+
+        mpz_clear(c);
+        break;
+
+    default:
+        ret = -1;
+        error_setg(errp, "Unknown padding");
+    }
+
+    return ret;
+}
+
+static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher,
+                                   const void *data, size_t data_len,
+                                   void *sig, size_t sig_len, Error **errp)
+{
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+    int ret;
+    mpz_t s;
+
+    /**
+     * The RSA algorithm cannot be used for signature/verification
+     * without padding.
+     */
+    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
+        error_setg(errp, "Try to make signature without padding");
+        return -1;
+    }
+
+    if (data_len > rsa->priv.size || sig_len != rsa->priv.size) {
+        error_setg(errp, "Invalid buffer size");
+        return -1;
+    }
+
+    mpz_init(s);
+    switch (rsa->hash_alg) {
+    case QCRYPTO_HASH_ALG_MD5:
+        ret = rsa_md5_sign_digest(&rsa->priv, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA1:
+        ret = rsa_sha1_sign_digest(&rsa->priv, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA256:
+        ret = rsa_sha256_sign_digest(&rsa->priv, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA512:
+        ret = rsa_sha512_sign_digest(&rsa->priv, data, s);
+        break;
+
+    default:
+        error_setg(errp, "Unknown hash algorithm");
+        ret = -1;
+        goto cleanup;
+    }
+
+    if (ret != 1) {
+        error_setg(errp, "Failed to make signature");
+        ret = -1;
+        goto cleanup;
+    }
+    nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
+    ret = sig_len;
+
+cleanup:
+    mpz_clear(s);
+
+    return ret;
+}
+
+static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher,
+                                     const void *sig, size_t sig_len,
+                                     const void *data, size_t data_len,
+                                     Error **errp)
+{
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+
+    int ret;
+    mpz_t s;
+
+    /**
+     * The RSA algorithm cannot be used for signature/verification
+     * without padding.
+     */
+    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
+        error_setg(errp, "Operation not supported");
+        return -1;
+    }
+    if (data_len > rsa->pub.size || sig_len < rsa->pub.size) {
+        error_setg(errp, "Invalid buffer size");
+        return -1;
+    }
+
+    nettle_mpz_init_set_str_256_u(s, sig_len, sig);
+    switch (rsa->hash_alg) {
+    case QCRYPTO_HASH_ALG_MD5:
+        ret = rsa_md5_verify_digest(&rsa->pub, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA1:
+        ret = rsa_sha1_verify_digest(&rsa->pub, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA256:
+        ret = rsa_sha256_verify_digest(&rsa->pub, data, s);
+        break;
+
+    case QCRYPTO_HASH_ALG_SHA512:
+        ret = rsa_sha512_verify_digest(&rsa->pub, data, s);
+        break;
+
+    default:
+        error_setg(errp, "Unsupported hash algorithm");
+        ret = -1;
+        goto cleanup;
+    }
+
+    if (ret != 1) {
+        error_setg(errp, "Failed to verify");
+        ret = -1;
+        goto cleanup;
+    }
+    ret = 0;
+
+cleanup:
+    mpz_clear(s);
+
+    return ret;
+}
+
+QCryptoAkCipherDriver nettle_rsa = {
+    .encrypt = qcrypto_nettle_rsa_encrypt,
+    .decrypt = qcrypto_nettle_rsa_decrypt,
+    .sign = qcrypto_nettle_rsa_sign,
+    .verify = qcrypto_nettle_rsa_verify,
+    .free = qcrypto_nettle_rsa_free,
+};
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+    const QCryptoAkCipherOptionsRSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key, size_t keylen,
+    Error **errp)
+{
+    QCryptoNettleRSA *rsa = g_new0(QCryptoNettleRSA, 1);
+
+    rsa->padding_alg = opt->padding_alg;
+    rsa->hash_alg = opt->hash_alg;
+    rsa->akcipher.driver = &nettle_rsa;
+    rsa_public_key_init(&rsa->pub);
+    rsa_private_key_init(&rsa->priv);
+
+    switch (type) {
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        if (qcrypt_nettle_parse_rsa_private_key(rsa, key, keylen) != 0) {
+            error_setg(errp, "Failed to parse rsa private key");
+            goto error;
+        }
+        break;
+
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        if (qcrypt_nettle_parse_rsa_public_key(rsa, key, keylen) != 0) {
+            error_setg(errp, "Failed to parse rsa public rsa key");
+            goto error;
+        }
+        break;
+
+    default:
+        error_setg(errp, "Unknown akcipher key type %d", type);
+        goto error;
+    }
+
+    return (QCryptoAkCipher *)rsa;
+
+error:
+    qcrypto_nettle_rsa_free((QCryptoAkCipher *)rsa);
+    return NULL;
+}
+
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+    switch (opts->alg) {
+    case QCRYPTO_AKCIPHER_ALG_RSA:
+        switch (opts->u.rsa.padding_alg) {
+        case QCRYPTO_RSA_PADDING_ALG_PKCS1:
+            switch (opts->u.rsa.hash_alg) {
+            case QCRYPTO_HASH_ALG_MD5:
+            case QCRYPTO_HASH_ALG_SHA1:
+            case QCRYPTO_HASH_ALG_SHA256:
+            case QCRYPTO_HASH_ALG_SHA512:
+                return true;
+
+            default:
+                return false;
+            }
+
+        case QCRYPTO_RSA_PADDING_ALG_RAW:
+        default:
+            return false;
+        }
+        break;
+
+    default:
+        return false;
+    }
+}
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index ab28bf415b..f287083f92 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -23,6 +23,9 @@
 #include "crypto/akcipher.h"
 #include "akcipherpriv.h"
 
+#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#include "akcipher-nettle.c.inc"
+#else
 QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
                                       QCryptoAkCipherKeyType type,
                                       const uint8_t *key, size_t keylen,
@@ -37,6 +40,7 @@ bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
 {
     return false;
 }
+#endif
 
 int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
                              const void *in, size_t in_len,
diff --git a/crypto/meson.build b/crypto/meson.build
index c9b36857a6..d9294eed83 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -21,10 +21,14 @@ crypto_ss.add(files(
   'tlscredspsk.c',
   'tlscredsx509.c',
   'tlssession.c',
+  'rsakey.c',
 ))
 
 if nettle.found()
   crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c'))
+  if hogweed.found()
+    crypto_ss.add(gmp, hogweed)
+  endif
   if xts == 'private'
     crypto_ss.add(files('xts.c'))
   endif
diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc
new file mode 100644
index 0000000000..0a93712f4f
--- /dev/null
+++ b/crypto/rsakey-builtin.c.inc
@@ -0,0 +1,209 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "der.h"
+#include "rsakey.h"
+
+static int extract_mpi(void *ctx, const uint8_t *value,
+                       size_t vlen, Error **errp)
+{
+    QCryptoAkCipherMPI *mpi = (QCryptoAkCipherMPI *)ctx;
+    if (vlen == 0) {
+        error_setg(errp, "Empty mpi field");
+        return -1;
+    }
+    mpi->data = g_memdup2(value, vlen);
+    mpi->len = vlen;
+    return 0;
+}
+
+static int extract_version(void *ctx, const uint8_t *value,
+                           size_t vlen, Error **errp)
+{
+    uint8_t *version = (uint8_t *)ctx;
+    if (vlen != 1 || *value > 1) {
+        error_setg(errp, "Invalid rsakey version");
+        return -1;
+    }
+    *version = *value;
+    return 0;
+}
+
+static int extract_seq_content(void *ctx, const uint8_t *value,
+                               size_t vlen, Error **errp)
+{
+    const uint8_t **content = (const uint8_t **)ctx;
+    if (vlen == 0) {
+        error_setg(errp, "Empty sequence");
+        return -1;
+    }
+    *content = value;
+    return 0;
+}
+
+/**
+ *
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse(
+    const uint8_t *key, size_t keylen)
+{
+    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+    const uint8_t *seq;
+    size_t seq_length;
+    int decode_ret;
+    Error *local_error = NULL;
+
+    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
+        extract_seq_content, &seq, &local_error);
+    if (decode_ret < 0) {
+        error_report_err(local_error);
+        goto error;
+    }
+    if (keylen != 0) {
+        goto error;
+    }
+    seq_length = decode_ret;
+
+    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+                               &rsa->n, &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+                               &rsa->e, &local_error) < 0) {
+        error_report_err(local_error);
+        goto error;
+    }
+    if (seq_length != 0) {
+        goto error;
+    }
+
+    return rsa;
+
+error:
+    qcrypto_akcipher_rsakey_free(rsa);
+    return NULL;
+}
+
+/**
+ *        RsaPrivKey ::= SEQUENCE {
+ *             version     INTEGER
+ *             n           INTEGER
+ *             e           INTEGER
+ *             d           INTEGER
+ *             p           INTEGER
+ *             q           INTEGER
+ *             dp          INTEGER
+ *             dq          INTEGER
+ *             u           INTEGER
+ *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
+ *         }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse(
+    const uint8_t *key, size_t keylen)
+{
+    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+    uint8_t version;
+    const uint8_t *seq;
+    int decode_ret;
+    size_t seq_length;
+    Error *local_error = NULL;
+
+    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
+        extract_seq_content, &seq, &local_error);
+    if (decode_ret < 0) {
+        error_report_err(local_error);
+        goto error;
+    }
+    if (keylen != 0) {
+        goto error;
+    }
+    seq_length = decode_ret;
+
+    decode_ret = qcrypto_der_decode_int(&seq, &seq_length, extract_version,
+                                        &version, &local_error);
+    if (decode_ret < 0) {
+        error_report_err(local_error);
+        goto error;
+    }
+
+    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+                               &rsa->n, &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+                               &rsa->e, &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+                               &rsa->d, &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p,
+                               &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q,
+                               &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp,
+                               &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq,
+                               &local_error) < 0 ||
+        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u,
+                               &local_error) < 0) {
+        error_report_err(local_error);
+        goto error;
+    }
+    /**
+     * According to the standard, otherPrimeInfos must be present for version 1.
+     * There is no strict verification here, this is to be compatible with
+     * the unit test of the kernel. TODO: remove this until linux kernel's
+     * unit-test is fixed.
+     */
+    if (version == 1 && seq_length != 0) {
+        if (qcrypto_der_decode_seq(&seq, &seq_length,
+                                   NULL, NULL, &local_error) < 0) {
+            error_report_err(local_error);
+            goto error;
+        }
+        if (seq_length != 0) {
+            goto error;
+        }
+        return rsa;
+    }
+    if (seq_length != 0) {
+        goto error;
+    }
+
+    return rsa;
+
+error:
+    qcrypto_akcipher_rsakey_free(rsa);
+    return NULL;
+}
+
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
+{
+    switch (type) {
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        return qcrypto_builtin_rsa_private_key_parse(key, keylen);
+
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        return qcrypto_builtin_rsa_public_key_parse(key, keylen);
+
+    default:
+        return NULL;
+    }
+}
diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
new file mode 100644
index 0000000000..2c89b3be88
--- /dev/null
+++ b/crypto/rsakey-nettle.c.inc
@@ -0,0 +1,154 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <nettle/asn1.h>
+#include <stdbool.h>
+
+#include "rsakey.h"
+
+static bool DumpMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
+{
+    mpi->data = g_memdup2(i->data, i->length);
+    mpi->len = i->length;
+    return true;
+}
+
+static bool GetMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
+{
+    if (asn1_der_iterator_next(i) != ASN1_ITERATOR_PRIMITIVE ||
+        i->type != ASN1_INTEGER) {
+        return false;
+    }
+    return DumpMPI(i, mpi);
+}
+
+
+/**
+ *        RsaPrivKey ::= SEQUENCE {
+ *             version     INTEGER
+ *             n           INTEGER
+ *             e           INTEGER
+ *             d           INTEGER
+ *             p           INTEGER
+ *             q           INTEGER
+ *             dp          INTEGER
+ *             dq          INTEGER
+ *             u           INTEGER
+ *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
+ *         }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_private_key_parse(
+    const uint8_t *key, size_t keylen)
+{
+    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+    struct asn1_der_iterator i;
+    uint32_t version;
+    int tag;
+
+    /* Parse entire struct */
+    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
+        i.type != ASN1_SEQUENCE ||
+        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
+        i.type != ASN1_INTEGER ||
+        !asn1_der_get_uint32(&i, &version) ||
+        version > 1 ||
+        !GetMPI(&i, &rsa->n) ||
+        !GetMPI(&i, &rsa->e) ||
+        !GetMPI(&i, &rsa->d) ||
+        !GetMPI(&i, &rsa->p) ||
+        !GetMPI(&i, &rsa->q) ||
+        !GetMPI(&i, &rsa->dp) ||
+        !GetMPI(&i, &rsa->dq) ||
+        !GetMPI(&i, &rsa->u)) {
+        goto error;
+    }
+
+    if (version == 1) {
+        tag = asn1_der_iterator_next(&i);
+        /**
+         * According to the standard otherPrimeInfos must be present for
+         * version 1. There is no strict verification here, this is to be
+         * compatible with the unit test of the kernel. TODO: remove this
+         * until linux-kernel's unit-test is fixed;
+         */
+        if (tag == ASN1_ITERATOR_END) {
+            return rsa;
+        }
+        if (tag != ASN1_ITERATOR_CONSTRUCTED ||
+            i.type != ASN1_SEQUENCE) {
+            goto error;
+        }
+    }
+
+    if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
+        goto error;
+    }
+
+    return rsa;
+
+error:
+    qcrypto_akcipher_rsakey_free(rsa);
+    return NULL;
+}
+
+/**
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_public_key_parse(
+    const uint8_t *key, size_t keylen)
+{
+
+    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+    struct asn1_der_iterator i;
+
+    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
+        i.type != ASN1_SEQUENCE ||
+        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
+        !DumpMPI(&i, &rsa->n) ||
+        !GetMPI(&i, &rsa->e) ||
+        asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
+        goto error;
+    }
+
+    return rsa;
+
+error:
+    qcrypto_akcipher_rsakey_free(rsa);
+    return NULL;
+}
+
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
+{
+    switch (type) {
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        return qcrypto_nettle_rsa_private_key_parse(key, keylen);
+
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        return qcrypto_nettle_rsa_public_key_parse(key, keylen);
+
+    default:
+        return NULL;
+    }
+}
diff --git a/crypto/rsakey.c b/crypto/rsakey.c
new file mode 100644
index 0000000000..cc40e072f0
--- /dev/null
+++ b/crypto/rsakey.c
@@ -0,0 +1,44 @@
+/*
+ * QEMU Crypto RSA key parser
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "rsakey.h"
+
+void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
+{
+    if (!rsa_key) {
+        return;
+    }
+    g_free(rsa_key->n.data);
+    g_free(rsa_key->e.data);
+    g_free(rsa_key->d.data);
+    g_free(rsa_key->p.data);
+    g_free(rsa_key->q.data);
+    g_free(rsa_key->dp.data);
+    g_free(rsa_key->dq.data);
+    g_free(rsa_key->u.data);
+    g_free(rsa_key);
+}
+
+#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#include "rsakey-nettle.c.inc"
+#else
+#include "rsakey-builtin.c.inc"
+#endif
diff --git a/crypto/rsakey.h b/crypto/rsakey.h
new file mode 100644
index 0000000000..a1e04ae021
--- /dev/null
+++ b/crypto/rsakey.h
@@ -0,0 +1,94 @@
+/*
+ * QEMU Crypto RSA key parser
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QCRYPTO_RSAKEY_H
+#define QCRYPTO_RSAKEY_H
+
+#include <nettle/bignum.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+
+typedef struct QCryptoAkCipherRSAKey QCryptoAkCipherRSAKey;
+typedef struct QCryptoAkCipherMPI QCryptoAkCipherMPI;
+
+/**
+ * Multiple precious integer, encoded as two' complement,
+ * copied directly from DER encoded ASN.1 structures.
+ */
+struct QCryptoAkCipherMPI {
+    uint8_t *data;
+    size_t len;
+};
+
+/* See rfc2437: https://datatracker.ietf.org/doc/html/rfc2437 */
+struct QCryptoAkCipherRSAKey {
+    /* The modulus */
+    QCryptoAkCipherMPI n;
+    /* The public exponent */
+    QCryptoAkCipherMPI e;
+    /* The private exponent */
+    QCryptoAkCipherMPI d;
+    /* The first factor */
+    QCryptoAkCipherMPI p;
+    /* The second factor */
+    QCryptoAkCipherMPI q;
+    /* The first factor's exponent */
+    QCryptoAkCipherMPI dp;
+    /* The second factor's exponent */
+    QCryptoAkCipherMPI dq;
+    /* The CRT coefficient */
+    QCryptoAkCipherMPI u;
+};
+
+/**
+ * Parse DER encoded ASN.1 RSA keys, expected ASN.1 schemas:
+ *        RsaPrivKey ::= SEQUENCE {
+ *             version     INTEGER
+ *             n           INTEGER
+ *             e           INTEGER
+ *             d           INTEGER
+ *             p           INTEGER
+ *             q           INTEGER
+ *             dp          INTEGER
+ *             dq          INTEGER
+ *             u           INTEGER
+ *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
+ *         }
+ *
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ *
+ * Returns: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key, size_t keylen);
+
+void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
+                              qcrypto_akcipher_rsakey_free);
+
+#endif
diff --git a/meson.build b/meson.build
index d083c6b7bf..fd0bf7aa5d 100644
--- a/meson.build
+++ b/meson.build
@@ -1049,6 +1049,7 @@ endif
 # gcrypt over nettle for performance reasons.
 gcrypt = not_found
 nettle = not_found
+hogweed = not_found
 xts = 'none'
 
 if get_option('nettle').enabled() and get_option('gcrypt').enabled()
@@ -1086,6 +1087,15 @@ if not gnutls_crypto.found()
   endif
 endif
 
+gmp = dependency('gmp', required: false, method: 'pkg-config', kwargs: static_kwargs)
+if nettle.found() and gmp.found()
+  hogweed = dependency('hogweed', version: '>=3.4',
+                       method: 'pkg-config',
+                       required: get_option('nettle'),
+                       kwargs: static_kwargs)
+endif
+
+
 gtk = not_found
 gtkx11 = not_found
 vte = not_found
@@ -1567,6 +1577,7 @@ config_host_data.set('CONFIG_GNUTLS', gnutls.found())
 config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found())
 config_host_data.set('CONFIG_GCRYPT', gcrypt.found())
 config_host_data.set('CONFIG_NETTLE', nettle.found())
+config_host_data.set('CONFIG_HOGWEED', hogweed.found())
 config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
 config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
 config_host_data.set('CONFIG_STATX', has_statx)
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (4 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-13 11:00   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher zhenwei pi
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang, cohuck

From: Lei He <helei.sig11@bytedance.com>

Added gcryt implementation of RSA algorithm, RSA algorithm
implemented by gcrypt has a higher priority than nettle because
it supports raw padding.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 crypto/akcipher-gcrypt.c.inc | 520 +++++++++++++++++++++++++++++++++++
 crypto/akcipher.c            |   4 +-
 2 files changed, 523 insertions(+), 1 deletion(-)
 create mode 100644 crypto/akcipher-gcrypt.c.inc

diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc
new file mode 100644
index 0000000000..32ff502f71
--- /dev/null
+++ b/crypto/akcipher-gcrypt.c.inc
@@ -0,0 +1,520 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <gcrypt.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoGcryptRSA {
+    QCryptoAkCipher akcipher;
+    gcry_sexp_t key;
+    QCryptoRSAPaddingAlgorithm padding_alg;
+    QCryptoHashAlgorithm hash_alg;
+} QCryptoGcryptRSA;
+
+static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher)
+{
+    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+    if (!rsa) {
+        return;
+    }
+
+    gcry_sexp_release(rsa->key);
+    g_free(rsa);
+}
+
+static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
+    const QCryptoAkCipherOptionsRSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key,  size_t keylen,
+    Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+                                      QCryptoAkCipherKeyType type,
+                                      const uint8_t *key, size_t keylen,
+                                      Error **errp)
+{
+    switch (opts->alg) {
+    case QCRYPTO_AKCIPHER_ALG_RSA:
+        return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new(
+            &opts->u.rsa, type, key, keylen, errp);
+
+    default:
+        error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+        return NULL;
+    }
+
+    return NULL;
+}
+
+static void qcrypto_gcrypt_set_rsa_size(QCryptoAkCipher *akcipher, gcry_mpi_t n)
+{
+    size_t key_size = (gcry_mpi_get_nbits(n) + 7) / 8;
+    akcipher->max_plaintext_len = key_size;
+    akcipher->max_ciphertext_len = key_size;
+    akcipher->max_dgst_len = key_size;
+    akcipher->max_signature_len = key_size;
+}
+
+static int qcrypto_gcrypt_parse_rsa_private_key(
+    QCryptoGcryptRSA *rsa,
+    const uint8_t *key, size_t keylen)
+{
+    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+        QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen);
+    gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
+    bool compute_mul_inv = false;
+    int ret = -1;
+    gcry_error_t err;
+
+    if (!rsa_key) {
+        return ret;
+    }
+
+    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
+                        rsa_key->n.data, rsa_key->n.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
+                        rsa_key->e.data, rsa_key->e.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_mpi_scan(&d, GCRYMPI_FMT_STD,
+                        rsa_key->d.data, rsa_key->d.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_mpi_scan(&p, GCRYMPI_FMT_STD,
+                        rsa_key->p.data, rsa_key->p.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_mpi_scan(&q, GCRYMPI_FMT_STD,
+                        rsa_key->q.data, rsa_key->q.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    if (gcry_mpi_cmp_ui(p, 0) > 0 && gcry_mpi_cmp_ui(q, 0) > 0) {
+        compute_mul_inv = true;
+
+        u = gcry_mpi_new(0);
+        if (gcry_mpi_cmp(p, q) > 0) {
+            gcry_mpi_swap(p, q);
+        }
+        gcry_mpi_invm(u, p, q);
+    }
+
+    if (compute_mul_inv) {
+        err = gcry_sexp_build(&rsa->key, NULL,
+            "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))",
+            n, e, d, p, q, u);
+    } else {
+        err = gcry_sexp_build(&rsa->key, NULL,
+            "(private-key (rsa (n %m) (e %m) (d %m)))", n, e, d);
+    }
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa,  n);
+    ret = 0;
+
+cleanup:
+    gcry_mpi_release(n);
+    gcry_mpi_release(e);
+    gcry_mpi_release(d);
+    gcry_mpi_release(p);
+    gcry_mpi_release(q);
+    gcry_mpi_release(u);
+    return ret;
+}
+
+static int qcrypto_gcrypt_parse_rsa_public_key(QCryptoGcryptRSA *rsa,
+                                               const uint8_t *key,
+                                               size_t keylen)
+{
+
+    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+        QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen);
+    gcry_mpi_t n = NULL, e = NULL;
+    int ret = -1;
+    gcry_error_t err;
+
+    if (!rsa_key) {
+        return ret;
+    }
+
+    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
+                        rsa_key->n.data, rsa_key->n.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
+                        rsa_key->e.data, rsa_key->e.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_sexp_build(&rsa->key, NULL,
+                          "(public-key (rsa (n %m) (e %m)))", n, e);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa, n);
+    ret = 0;
+
+cleanup:
+    gcry_mpi_release(n);
+    gcry_mpi_release(e);
+    return ret;
+}
+
+static int qcrypto_gcrypt_rsa_encrypt(QCryptoAkCipher *akcipher,
+                                      const void *in, size_t in_len,
+                                      void *out, size_t out_len,
+                                      Error **errp)
+{
+    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+    int ret = -1;
+    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
+    gcry_sexp_t cipher_sexp_item = NULL;
+    gcry_mpi_t cipher_mpi = NULL;
+    const char *result;
+    gcry_error_t err;
+    size_t actual_len;
+
+    if (in_len > akcipher->max_plaintext_len) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    err = gcry_sexp_build(&data_sexp, NULL,
+                          "(data (flags %s) (value %b))",
+                          QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg),
+                          in_len, in);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_pk_encrypt(&cipher_sexp, data_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    /* S-expression of cipher: (enc-val (rsa (a a-mpi))) */
+    cipher_sexp_item = gcry_sexp_find_token(cipher_sexp, "a", 0);
+    if (!cipher_sexp_item || gcry_sexp_length(cipher_sexp_item) != 2) {
+        goto cleanup;
+    }
+
+    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
+        cipher_mpi = gcry_sexp_nth_mpi(cipher_sexp_item, 1, GCRYMPI_FMT_USG);
+        if (!cipher_mpi) {
+            goto cleanup;
+        }
+        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
+                             &actual_len, cipher_mpi);
+        if (gcry_err_code(err) != 0 || actual_len > out_len) {
+            goto cleanup;
+        }
+
+        /* We always padding leading-zeros for RSA-RAW */
+        if (actual_len < out_len) {
+            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
+            memset(out, 0, out_len - actual_len);
+        }
+        ret = out_len;
+
+    } else {
+        result = gcry_sexp_nth_data(cipher_sexp_item, 1, &actual_len);
+        if (!result || actual_len > out_len) {
+            goto cleanup;
+        }
+        memcpy(out, result, actual_len);
+        ret = actual_len;
+    }
+
+cleanup:
+    gcry_sexp_release(data_sexp);
+    gcry_sexp_release(cipher_sexp);
+    gcry_sexp_release(cipher_sexp_item);
+    gcry_mpi_release(cipher_mpi);
+    return ret;
+}
+
+static int qcrypto_gcrypt_rsa_decrypt(QCryptoAkCipher *akcipher,
+                                      const void *in, size_t in_len,
+                                      void *out, size_t out_len,
+                                      Error **errp)
+{
+    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+    int ret = -1;
+    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
+    gcry_mpi_t data_mpi = NULL;
+    gcry_error_t err;
+    size_t actual_len;
+    const char *result;
+
+    if (in_len > akcipher->max_ciphertext_len) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    err = gcry_sexp_build(&cipher_sexp, NULL,
+                          "(enc-val (flags %s) (rsa (a %b) ))",
+                          QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg),
+                          in_len, in);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_pk_decrypt(&data_sexp, cipher_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    /* S-expression of cipher: (value plaintext) */
+    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
+        data_mpi = gcry_sexp_nth_mpi(data_sexp, 1, GCRYMPI_FMT_USG);
+        if (!data_mpi) {
+            goto cleanup;
+        }
+        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
+                             &actual_len, data_mpi);
+        if (gcry_err_code(err) != 0) {
+            goto cleanup;
+        }
+         if (actual_len > out_len) {
+            goto cleanup;
+        }
+        /* We always padding leading-zeros for RSA-RAW */
+        if (actual_len < out_len) {
+            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
+            memset(out, 0, out_len - actual_len);
+        }
+        ret = out_len;
+    } else {
+        result = gcry_sexp_nth_data(data_sexp, 1, &actual_len);
+        if (!result || actual_len > out_len) {
+            goto cleanup;
+        }
+        memcpy(out, result, actual_len);
+        ret = actual_len;
+    }
+
+cleanup:
+    gcry_sexp_release(cipher_sexp);
+    gcry_sexp_release(data_sexp);
+    gcry_mpi_release(data_mpi);
+    return ret;
+}
+
+static int qcrypto_gcrypt_rsa_sign(QCryptoAkCipher *akcipher,
+                                   const void *in, size_t in_len,
+                                   void *out, size_t out_len, Error **errp)
+{
+    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+    int ret = -1;
+    gcry_sexp_t dgst_sexp = NULL, sig_sexp = NULL;
+    gcry_sexp_t sig_sexp_item = NULL;
+    const char *result;
+    gcry_error_t err;
+    size_t actual_len;
+
+    if (in_len > akcipher->max_dgst_len) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) {
+        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
+        return ret;
+    }
+    err = gcry_sexp_build(&dgst_sexp, NULL,
+                          "(data (flags pkcs1) (hash %s %b))",
+                          QCryptoHashAlgorithm_str(rsa->hash_alg),
+                          in_len, in);
+
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_pk_sign(&sig_sexp, dgst_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    /* S-expression of signature: (sig-val (rsa (s s-mpi))) */
+    sig_sexp_item = gcry_sexp_find_token(sig_sexp, "s", 0);
+    if (!sig_sexp_item || gcry_sexp_length(sig_sexp_item) != 2) {
+        goto cleanup;
+    }
+
+    result = gcry_sexp_nth_data(sig_sexp_item, 1, &actual_len);
+    if (!result || actual_len > out_len) {
+        goto cleanup;
+    }
+    memcpy(out, result, actual_len);
+    ret = actual_len;
+
+cleanup:
+    gcry_sexp_release(dgst_sexp);
+    gcry_sexp_release(sig_sexp);
+    gcry_sexp_release(sig_sexp_item);
+
+    return ret;
+}
+
+static int qcrypto_gcrypt_rsa_verify(QCryptoAkCipher *akcipher,
+                                     const void *in, size_t in_len,
+                                     const void *in2, size_t in2_len,
+                                     Error **errp)
+{
+    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
+    int ret = -1;
+    gcry_sexp_t sig_sexp = NULL, dgst_sexp = NULL;
+    gcry_error_t err;
+
+    if (in_len > akcipher->max_signature_len ||
+        in2_len > akcipher->max_dgst_len) {
+        error_setg(errp, "Invalid buffer size");
+        return ret;
+    }
+
+    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) {
+        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
+        return ret;
+    }
+
+    err = gcry_sexp_build(&sig_sexp, NULL,
+                          "(sig-val (rsa (s %b)))", in_len, in);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_sexp_build(&dgst_sexp, NULL,
+                          "(data (flags pkcs1) (hash %s %b))",
+                          QCryptoHashAlgorithm_str(rsa->hash_alg),
+                          in2_len, in2);
+    if (gcry_err_code(err) != 0) {
+        goto cleanup;
+    }
+
+    err = gcry_pk_verify(sig_sexp, dgst_sexp, rsa->key);
+    if (gcry_err_code(err) == 0) {
+        ret = 0;
+    }
+
+cleanup:
+    gcry_sexp_release(dgst_sexp);
+    gcry_sexp_release(sig_sexp);
+
+    return ret;
+}
+
+QCryptoAkCipherDriver gcrypt_rsa = {
+    .encrypt = qcrypto_gcrypt_rsa_encrypt,
+    .decrypt = qcrypto_gcrypt_rsa_decrypt,
+    .sign = qcrypto_gcrypt_rsa_sign,
+    .verify = qcrypto_gcrypt_rsa_verify,
+    .free = qcrypto_gcrypt_rsa_free,
+};
+
+static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
+    const QCryptoAkCipherOptionsRSA *opt,
+    QCryptoAkCipherKeyType type,
+    const uint8_t *key, size_t keylen,
+    Error **errp)
+{
+    QCryptoGcryptRSA *rsa = g_new0(QCryptoGcryptRSA, 1);
+    rsa->padding_alg = opt->padding_alg;
+    rsa->hash_alg = opt->hash_alg;
+    rsa->akcipher.driver = &gcrypt_rsa;
+
+    switch (type) {
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        if (qcrypto_gcrypt_parse_rsa_private_key(rsa, key, keylen) != 0) {
+            error_setg(errp, "Failed to parse rsa private key");
+            goto error;
+        }
+        break;
+
+    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        if (qcrypto_gcrypt_parse_rsa_public_key(rsa, key, keylen) != 0) {
+            error_setg(errp, "Failed to parse rsa public rsa key");
+            goto error;
+        }
+        break;
+
+    default:
+        error_setg(errp, "Unknown akcipher key type %d", type);
+        goto error;
+    }
+
+    return rsa;
+
+error:
+    qcrypto_gcrypt_rsa_free((QCryptoAkCipher *)rsa);
+    return NULL;
+}
+
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+    switch (opts->alg) {
+    case QCRYPTO_AKCIPHER_ALG_RSA:
+        switch (opts->u.rsa.padding_alg) {
+        case QCRYPTO_RSA_PADDING_ALG_RAW:
+            return true;
+
+        case QCRYPTO_RSA_PADDING_ALG_PKCS1:
+            switch (opts->u.rsa.hash_alg) {
+            case QCRYPTO_HASH_ALG_MD5:
+            case QCRYPTO_HASH_ALG_SHA1:
+            case QCRYPTO_HASH_ALG_SHA256:
+            case QCRYPTO_HASH_ALG_SHA512:
+                return true;
+
+            default:
+                return false;
+            }
+
+        default:
+            return false;
+        }
+
+    default:
+        return true;
+    }
+}
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index f287083f92..ad88379c1e 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -23,7 +23,9 @@
 #include "crypto/akcipher.h"
 #include "akcipherpriv.h"
 
-#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#if defined(CONFIG_GCRYPT)
+#include "akcipher-gcrypt.c.inc"
+#elif defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
 #include "akcipher-nettle.c.inc"
 #else
 QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (5 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-12  9:38   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys zhenwei pi
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

From: Lei He <helei.sig11@bytedance.com>

Add unit test and benchmark test for crypto akcipher.

Signed-off-by: lei he <helei.sig11@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 tests/bench/benchmark-crypto-akcipher.c | 157 ++++++
 tests/bench/meson.build                 |   4 +
 tests/bench/test_akcipher_keys.inc      | 537 ++++++++++++++++++
 tests/unit/meson.build                  |   1 +
 tests/unit/test-crypto-akcipher.c       | 711 ++++++++++++++++++++++++
 5 files changed, 1410 insertions(+)
 create mode 100644 tests/bench/benchmark-crypto-akcipher.c
 create mode 100644 tests/bench/test_akcipher_keys.inc
 create mode 100644 tests/unit/test-crypto-akcipher.c

diff --git a/tests/bench/benchmark-crypto-akcipher.c b/tests/bench/benchmark-crypto-akcipher.c
new file mode 100644
index 0000000000..c6c80c0be1
--- /dev/null
+++ b/tests/bench/benchmark-crypto-akcipher.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU Crypto akcipher speed benchmark
+ *
+ * Copyright (c) 2022 Bytedance
+ *
+ * Authors:
+ *    lei he <helei.sig11@bytedance.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/init.h"
+#include "crypto/akcipher.h"
+#include "standard-headers/linux/virtio_crypto.h"
+
+#include "test_akcipher_keys.inc"
+
+static bool keep_running;
+
+static void alarm_handler(int sig)
+{
+    keep_running = false;
+}
+
+static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key,
+                                            size_t keylen,
+                                            QCryptoRSAPaddingAlgorithm padding,
+                                            QCryptoHashAlgorithm hash)
+{
+    QCryptoAkCipherOptions opt;
+    QCryptoAkCipher *rsa;
+
+    opt.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+    opt.u.rsa.padding_alg = padding;
+    opt.u.rsa.hash_alg = hash;
+    rsa = qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+                               priv_key, keylen, &error_abort);
+    return rsa;
+}
+
+static void test_rsa_speed(const uint8_t *priv_key, size_t keylen,
+                           size_t key_size)
+{
+#define BYTE 8
+#define SHA1_DGST_LEN 20
+#define DURATION_SECONDS 10
+#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1
+#define HASH QCRYPTO_HASH_ALG_SHA1
+
+    g_autoptr(QCryptoAkCipher) rsa =
+        create_rsa_akcipher(priv_key, keylen, PADDING, HASH);
+    g_autofree uint8_t *dgst = NULL;
+    g_autofree uint8_t *signature = NULL;
+    size_t count;
+
+    dgst = g_new0(uint8_t, SHA1_DGST_LEN);
+    memset(dgst, g_test_rand_int(), SHA1_DGST_LEN);
+    signature = g_new0(uint8_t, key_size / BYTE);
+
+    g_test_message("benchmark rsa%lu (%s-%s) sign in %d seconds", key_size,
+                   QCryptoRSAPaddingAlgorithm_str(PADDING),
+                   QCryptoHashAlgorithm_str(HASH),
+                   DURATION_SECONDS);
+    alarm(DURATION_SECONDS);
+    g_test_timer_start();
+    for (keep_running = true, count = 0; keep_running; ++count) {
+        g_assert(qcrypto_akcipher_sign(rsa, dgst, SHA1_DGST_LEN,
+                                       signature, key_size / BYTE,
+                                       &error_abort) > 0);
+    }
+    g_test_timer_elapsed();
+    g_test_message("rsa%lu (%s-%s) sign %lu times in %.2f seconds,"
+                   " %.2f times/sec ",
+                   key_size,  QCryptoRSAPaddingAlgorithm_str(PADDING),
+                   QCryptoHashAlgorithm_str(HASH),
+                   count, g_test_timer_last(),
+                   (double)count / g_test_timer_last());
+
+    g_test_message("benchmark rsa%lu (%s-%s) verify in %d seconds", key_size,
+                   QCryptoRSAPaddingAlgorithm_str(PADDING),
+                   QCryptoHashAlgorithm_str(HASH),
+                   DURATION_SECONDS);
+    alarm(DURATION_SECONDS);
+    g_test_timer_start();
+    for (keep_running = true, count = 0; keep_running; ++count) {
+        g_assert(qcrypto_akcipher_verify(rsa, signature, key_size / BYTE,
+                                         dgst, SHA1_DGST_LEN,
+                                         &error_abort) == 0);
+    }
+    g_test_timer_elapsed();
+    g_test_message("rsa%lu (%s-%s) verify %lu times in %.2f seconds,"
+                   " %.2f times/sec ",
+                   key_size, QCryptoRSAPaddingAlgorithm_str(PADDING),
+                   QCryptoHashAlgorithm_str(HASH),
+                   count, g_test_timer_last(),
+                   (double)count / g_test_timer_last());
+}
+
+static void test_rsa_1024_speed(const void *opaque)
+{
+    size_t key_size = (size_t)opaque;
+    test_rsa_speed(rsa1024_priv_key, sizeof(rsa1024_priv_key), key_size);
+}
+
+static void test_rsa_2048_speed(const void *opaque)
+{
+    size_t key_size = (size_t)opaque;
+    test_rsa_speed(rsa2048_priv_key, sizeof(rsa2048_priv_key), key_size);
+}
+
+static void test_rsa_4096_speed(const void *opaque)
+{
+    size_t key_size = (size_t)opaque;
+    test_rsa_speed(rsa4096_priv_key, sizeof(rsa4096_priv_key), key_size);
+}
+
+int main(int argc, char **argv)
+{
+    char *alg = NULL;
+    char *size = NULL;
+    g_test_init(&argc, &argv, NULL);
+    g_assert(qcrypto_init(NULL) == 0);
+    struct sigaction new_action, old_action;
+
+    new_action.sa_handler = alarm_handler;
+
+    /* Set up the structure to specify the new action. */
+    sigemptyset(&new_action.sa_mask);
+    new_action.sa_flags = 0;
+    sigaction(SIGALRM, NULL, &old_action);
+    g_assert(old_action.sa_handler != SIG_IGN);
+    sigaction(SIGALRM, &new_action, NULL);
+
+#define ADD_TEST(asym_alg, keysize)                    \
+    if ((!alg || g_str_equal(alg, #asym_alg)) &&       \
+        (!size || g_str_equal(size, #keysize)))        \
+        g_test_add_data_func(                          \
+        "/crypto/akcipher/" #asym_alg "-" #keysize,    \
+        (void *)keysize,                               \
+        test_ ## asym_alg ## _ ## keysize ## _speed)
+
+    if (argc >= 2) {
+        alg = argv[1];
+    }
+    if (argc >= 3) {
+        size = argv[2];
+    }
+
+    ADD_TEST(rsa, 1024);
+    ADD_TEST(rsa, 2048);
+    ADD_TEST(rsa, 4096);
+
+    return g_test_run();
+}
diff --git a/tests/bench/meson.build b/tests/bench/meson.build
index 00b3c209dc..f793d972b6 100644
--- a/tests/bench/meson.build
+++ b/tests/bench/meson.build
@@ -23,6 +23,10 @@ if have_block
   }
 endif
 
+benchs += {
+    'benchmark-crypto-akcipher': [crypto],
+}
+
 foreach bench_name, deps: benchs
   exe = executable(bench_name, bench_name + '.c',
                    dependencies: [qemuutil] + deps)
diff --git a/tests/bench/test_akcipher_keys.inc b/tests/bench/test_akcipher_keys.inc
new file mode 100644
index 0000000000..df3eccb45e
--- /dev/null
+++ b/tests/bench/test_akcipher_keys.inc
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2022 Bytedance, and/or its affiliates
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Author: lei he <helei.sig11@bytedance.com>
+ */
+
+/* RSA test keys, generated by OpenSSL */
+static const uint8_t rsa1024_priv_key[] = {
+    0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02,
+    0x81, 0x81, 0x00, 0xe6, 0x4d, 0x76, 0x4f, 0xb2,
+    0x97, 0x09, 0xad, 0x9d, 0x17, 0x33, 0xf2, 0x30,
+    0x42, 0x83, 0xa9, 0xcb, 0x49, 0xa4, 0x2e, 0x59,
+    0x5e, 0x75, 0x51, 0xd1, 0xac, 0xc8, 0x86, 0x3e,
+    0xdb, 0x72, 0x2e, 0xb2, 0xf7, 0xc3, 0x5b, 0xc7,
+    0xea, 0xed, 0x30, 0xd1, 0xf7, 0x37, 0xee, 0x9d,
+    0x36, 0x59, 0x6f, 0xf8, 0xce, 0xc0, 0x5c, 0x82,
+    0x80, 0x37, 0x83, 0xd7, 0x45, 0x6a, 0xe9, 0xea,
+    0xc5, 0x3a, 0x59, 0x6b, 0x34, 0x31, 0x44, 0x00,
+    0x74, 0xa7, 0x29, 0xab, 0x79, 0x4a, 0xbd, 0xe8,
+    0x25, 0x35, 0x01, 0x11, 0x40, 0xbf, 0x31, 0xbd,
+    0xd3, 0xe0, 0x68, 0x1e, 0xd5, 0x5b, 0x2f, 0xe9,
+    0x20, 0xf2, 0x9f, 0x46, 0x35, 0x30, 0xa8, 0xf1,
+    0xfe, 0xef, 0xd8, 0x76, 0x23, 0x46, 0x34, 0x70,
+    0xa1, 0xce, 0xc6, 0x65, 0x6d, 0xb0, 0x94, 0x7e,
+    0xe5, 0x92, 0x45, 0x7b, 0xaa, 0xbb, 0x95, 0x97,
+    0x77, 0xcd, 0xd3, 0x02, 0x03, 0x01, 0x00, 0x01,
+    0x02, 0x81, 0x80, 0x30, 0x6a, 0xc4, 0x9e, 0xc8,
+    0xba, 0xfc, 0x2b, 0xe5, 0xc4, 0xc5, 0x04, 0xfb,
+    0xa4, 0x60, 0x2d, 0xc8, 0x31, 0x39, 0x35, 0x0d,
+    0x50, 0xd0, 0x75, 0x5d, 0x11, 0x68, 0x2e, 0xe0,
+    0xf4, 0x1d, 0xb3, 0x37, 0xa8, 0xe3, 0x07, 0x5e,
+    0xa6, 0x43, 0x2b, 0x6a, 0x59, 0x01, 0x07, 0x47,
+    0x41, 0xef, 0xd7, 0x9c, 0x85, 0x4a, 0xe7, 0xa7,
+    0xff, 0xf0, 0xab, 0xe5, 0x0c, 0x11, 0x08, 0x10,
+    0x75, 0x5a, 0x68, 0xa0, 0x08, 0x03, 0xc9, 0x40,
+    0x79, 0x67, 0x1d, 0x65, 0x89, 0x2d, 0x08, 0xf9,
+    0xb5, 0x1b, 0x7d, 0xd2, 0x41, 0x3b, 0x33, 0xf2,
+    0x47, 0x2f, 0x9c, 0x0b, 0xd5, 0xaf, 0xcb, 0xdb,
+    0xbb, 0x37, 0x63, 0x03, 0xf8, 0xe7, 0x2e, 0xc7,
+    0x3c, 0x86, 0x9f, 0xc2, 0x9b, 0xb4, 0x70, 0x6a,
+    0x4d, 0x7c, 0xe4, 0x1b, 0x3a, 0xa9, 0xae, 0xd7,
+    0xce, 0x7f, 0x56, 0xc2, 0x73, 0x5e, 0x58, 0x63,
+    0xd5, 0x86, 0x41, 0x02, 0x41, 0x00, 0xf6, 0x56,
+    0x69, 0xec, 0xef, 0x65, 0x95, 0xdc, 0x25, 0x47,
+    0xe0, 0x6f, 0xb0, 0x4f, 0x79, 0x77, 0x0a, 0x5e,
+    0x46, 0xcb, 0xbd, 0x0b, 0x71, 0x51, 0x2a, 0xa4,
+    0x65, 0x29, 0x18, 0xc6, 0x30, 0xa0, 0x95, 0x4c,
+    0x4b, 0xbe, 0x8c, 0x40, 0xe3, 0x9c, 0x23, 0x02,
+    0x14, 0x43, 0xe9, 0x64, 0xea, 0xe3, 0xa8, 0xe2,
+    0x1a, 0xd5, 0xf9, 0x5c, 0xe0, 0x36, 0x2c, 0x97,
+    0xda, 0xd5, 0xc7, 0x46, 0xce, 0x11, 0x02, 0x41,
+    0x00, 0xef, 0x56, 0x08, 0xb8, 0x29, 0xa5, 0xa6,
+    0x7c, 0xf7, 0x5f, 0xb4, 0xf5, 0x63, 0xe7, 0xeb,
+    0x45, 0xfd, 0x89, 0xaa, 0x94, 0xa6, 0x3d, 0x0b,
+    0xd9, 0x04, 0x6f, 0x78, 0xe0, 0xbb, 0xa2, 0xd4,
+    0x29, 0x83, 0x17, 0x95, 0x6f, 0x50, 0x3d, 0x40,
+    0x5d, 0xe5, 0x24, 0xda, 0xc2, 0x23, 0x50, 0x86,
+    0xa8, 0x34, 0xc8, 0x6f, 0xec, 0x7f, 0xb6, 0x45,
+    0x3a, 0xdd, 0x78, 0x9b, 0xee, 0xa1, 0xe4, 0x09,
+    0xa3, 0x02, 0x40, 0x5c, 0xd6, 0x66, 0x67, 0x58,
+    0x35, 0xc5, 0xcb, 0xc8, 0xf5, 0x14, 0xbd, 0xa3,
+    0x09, 0xe0, 0xb2, 0x1f, 0x63, 0x36, 0x75, 0x34,
+    0x52, 0xea, 0xaa, 0xf7, 0x52, 0x2b, 0x99, 0xd8,
+    0x6f, 0x61, 0x06, 0x34, 0x1e, 0x23, 0xf1, 0xb5,
+    0x34, 0x03, 0x53, 0xe5, 0xd1, 0xb3, 0xc7, 0x80,
+    0x5f, 0x7b, 0x32, 0xbf, 0x84, 0x2f, 0x2e, 0xf3,
+    0x22, 0xb0, 0x91, 0x5a, 0x2f, 0x04, 0xd7, 0x4a,
+    0x9a, 0x01, 0xb1, 0x02, 0x40, 0x34, 0x0b, 0x26,
+    0x4c, 0x3d, 0xaa, 0x2a, 0xc0, 0xe3, 0xdd, 0xe8,
+    0xf0, 0xaf, 0x6f, 0xe0, 0x06, 0x51, 0x32, 0x9d,
+    0x68, 0x43, 0x99, 0xe4, 0xb8, 0xa5, 0x31, 0x44,
+    0x3c, 0xc2, 0x30, 0x8f, 0x28, 0x13, 0xbc, 0x8e,
+    0x1f, 0x2d, 0x78, 0x94, 0x45, 0x96, 0xad, 0x63,
+    0xf0, 0x71, 0x53, 0x72, 0x64, 0xa3, 0x4d, 0xae,
+    0xa0, 0xe3, 0xc8, 0x93, 0xd7, 0x50, 0x0f, 0x89,
+    0x00, 0xe4, 0x2d, 0x3d, 0x37, 0x02, 0x41, 0x00,
+    0xbe, 0xa6, 0x08, 0xe0, 0xc8, 0x15, 0x2a, 0x47,
+    0xcb, 0xd5, 0xec, 0x93, 0xd3, 0xaa, 0x12, 0x82,
+    0xaf, 0xac, 0x51, 0x5a, 0x5b, 0xa7, 0x93, 0x4b,
+    0xb9, 0xab, 0x00, 0xfa, 0x5a, 0xea, 0x34, 0xe4,
+    0x80, 0xf1, 0x44, 0x6a, 0x65, 0xe4, 0x33, 0x99,
+    0xfb, 0x54, 0xd7, 0x89, 0x5a, 0x1b, 0xd6, 0x2b,
+    0xcc, 0x6e, 0x4b, 0x19, 0xa0, 0x6d, 0x93, 0x9f,
+    0xc3, 0x91, 0x7a, 0xa5, 0xd8, 0x59, 0x0e, 0x9e,
+};
+
+static const uint8_t rsa2048_priv_key[] = {
+    0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02,
+    0x82, 0x01, 0x01, 0x00, 0xbd, 0x9c, 0x83, 0x6b,
+    0x0e, 0x8e, 0xcf, 0xfa, 0xaa, 0x4f, 0x6a, 0xf4,
+    0xe3, 0x52, 0x0f, 0xa5, 0xd0, 0xbe, 0x5e, 0x7f,
+    0x08, 0x24, 0xba, 0x87, 0x46, 0xfb, 0x28, 0x93,
+    0xe5, 0xe5, 0x81, 0x42, 0xc0, 0xf9, 0x17, 0xc7,
+    0x81, 0x01, 0xf4, 0x18, 0x6a, 0x17, 0xf5, 0x57,
+    0x20, 0x37, 0xcf, 0xf9, 0x74, 0x5e, 0xe1, 0x48,
+    0x6a, 0x71, 0x0a, 0x0f, 0x79, 0x72, 0x2b, 0x46,
+    0x10, 0x53, 0xdc, 0x14, 0x43, 0xbd, 0xbc, 0x6d,
+    0x15, 0x6f, 0x15, 0x4e, 0xf0, 0x0d, 0x89, 0x39,
+    0x02, 0xc3, 0x68, 0x5c, 0xa8, 0xfc, 0xed, 0x64,
+    0x9d, 0x98, 0xb7, 0xcd, 0x83, 0x66, 0x93, 0xc3,
+    0xd9, 0x57, 0xa0, 0x21, 0x93, 0xad, 0x5c, 0x75,
+    0x69, 0x88, 0x9e, 0x81, 0xdc, 0x7f, 0x1d, 0xd5,
+    0xbd, 0x1c, 0xc1, 0x30, 0x56, 0xa5, 0xda, 0x99,
+    0x46, 0xa6, 0x6d, 0x0e, 0x6f, 0x5e, 0x51, 0x34,
+    0x49, 0x73, 0xc3, 0x67, 0x49, 0x7e, 0x21, 0x2a,
+    0x20, 0xa7, 0x2b, 0x92, 0x73, 0x1d, 0xa5, 0x25,
+    0x2a, 0xd0, 0x3a, 0x89, 0x75, 0xb2, 0xbb, 0x19,
+    0x37, 0x78, 0x48, 0xd2, 0xf2, 0x2a, 0x6d, 0x9e,
+    0xc6, 0x26, 0xca, 0x46, 0x8c, 0xf1, 0x42, 0x2a,
+    0x31, 0xb2, 0xfc, 0xe7, 0x55, 0x51, 0xff, 0x07,
+    0x13, 0x5b, 0x36, 0x59, 0x2b, 0x43, 0x30, 0x4b,
+    0x05, 0x5c, 0xd2, 0x45, 0xa0, 0xa0, 0x7c, 0x17,
+    0x5b, 0x07, 0xbb, 0x5d, 0x83, 0x80, 0x92, 0x6d,
+    0x87, 0x1a, 0x43, 0xac, 0xc7, 0x6b, 0x8d, 0x11,
+    0x60, 0x27, 0xd2, 0xdf, 0xdb, 0x71, 0x02, 0x55,
+    0x6e, 0xb5, 0xca, 0x4d, 0xda, 0x59, 0x0d, 0xb8,
+    0x8c, 0xcd, 0xd3, 0x0e, 0x55, 0xa0, 0xa4, 0x8d,
+    0xa0, 0x14, 0x10, 0x48, 0x42, 0x35, 0x56, 0x08,
+    0xf7, 0x29, 0x5f, 0xa2, 0xea, 0xa4, 0x5e, 0x8e,
+    0x99, 0x56, 0xaa, 0x5a, 0x8c, 0x23, 0x8f, 0x35,
+    0x22, 0x8a, 0xff, 0xed, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0x02, 0x82, 0x01, 0x00, 0x4e, 0x4a, 0xf3,
+    0x44, 0xe0, 0x64, 0xfd, 0xe1, 0xde, 0x33, 0x1e,
+    0xd1, 0xf1, 0x8f, 0x6f, 0xe0, 0xa2, 0xfa, 0x08,
+    0x60, 0xe1, 0xc6, 0xf0, 0xb2, 0x6d, 0x0f, 0xc6,
+    0x28, 0x93, 0xb4, 0x19, 0x94, 0xab, 0xc3, 0xef,
+    0x1a, 0xb4, 0xdd, 0x4e, 0xa2, 0x4a, 0x24, 0x8c,
+    0x6c, 0xa6, 0x64, 0x05, 0x5f, 0x56, 0xba, 0xda,
+    0xc1, 0x21, 0x1a, 0x7d, 0xf1, 0xf7, 0xce, 0xb9,
+    0xa9, 0x9b, 0x92, 0x54, 0xfc, 0x95, 0x20, 0x22,
+    0x4e, 0xd4, 0x9b, 0xe2, 0xab, 0x8e, 0x99, 0xb8,
+    0x40, 0xaf, 0x30, 0x6a, 0xc6, 0x60, 0x0c, 0xd8,
+    0x25, 0x44, 0xa1, 0xcb, 0xbb, 0x73, 0x77, 0x86,
+    0xaa, 0x46, 0xf3, 0x54, 0xae, 0xa8, 0xa0, 0xdb,
+    0xdd, 0xab, 0x6e, 0xfb, 0x2c, 0x5a, 0x14, 0xaf,
+    0x08, 0x13, 0xa7, 0x6c, 0xe9, 0xfd, 0xcd, 0x4c,
+    0x1f, 0x20, 0x3a, 0x16, 0x2b, 0xf0, 0xb6, 0x7c,
+    0x47, 0x5f, 0xd1, 0x0a, 0x2c, 0xc4, 0xa5, 0x68,
+    0xd0, 0x43, 0x75, 0x6b, 0x65, 0xaa, 0x32, 0xc6,
+    0x99, 0x06, 0xcb, 0x8f, 0xe6, 0x8d, 0xce, 0xbf,
+    0x4d, 0x0d, 0x7b, 0x22, 0x2a, 0x8a, 0xcb, 0x7d,
+    0x7f, 0x16, 0x48, 0x85, 0xf1, 0x86, 0xcb, 0x54,
+    0xb9, 0x39, 0xd4, 0xbc, 0xe3, 0x2d, 0x27, 0x59,
+    0xf6, 0x81, 0x5e, 0x94, 0x45, 0xdf, 0xb9, 0x22,
+    0xaf, 0x64, 0x0d, 0x14, 0xec, 0x8c, 0xeb, 0x71,
+    0xac, 0xee, 0x09, 0x4c, 0xbf, 0x34, 0xf9, 0xf4,
+    0x66, 0x77, 0x36, 0x3b, 0x41, 0x74, 0x01, 0x4f,
+    0xfc, 0x56, 0x83, 0xba, 0x14, 0xb0, 0x2f, 0xdd,
+    0x4d, 0xb9, 0x3f, 0xdf, 0x71, 0xbe, 0x7b, 0xba,
+    0x66, 0xc8, 0xc5, 0x42, 0xc9, 0xba, 0x18, 0x63,
+    0x45, 0x07, 0x2f, 0x84, 0x3e, 0xc3, 0xfb, 0x47,
+    0xda, 0xd4, 0x1d, 0x0e, 0x9d, 0x96, 0xc0, 0xea,
+    0xee, 0x45, 0x2f, 0xe1, 0x62, 0x23, 0xee, 0xef,
+    0x3d, 0x5e, 0x55, 0xa1, 0x0d, 0x02, 0x81, 0x81,
+    0x00, 0xeb, 0x76, 0x88, 0xd3, 0xae, 0x3f, 0x1d,
+    0xf2, 0x49, 0xe0, 0x37, 0x49, 0x83, 0x82, 0x6c,
+    0xf7, 0xf1, 0x17, 0x30, 0x75, 0x2e, 0x89, 0x06,
+    0x88, 0x56, 0x32, 0xf6, 0xfa, 0x58, 0xcb, 0x3c,
+    0x98, 0x67, 0xc3, 0xde, 0x10, 0x82, 0xe5, 0xfa,
+    0xfa, 0x52, 0x47, 0x8d, 0xd7, 0x00, 0xc6, 0xcb,
+    0xf7, 0xf6, 0x57, 0x9b, 0x6e, 0x0c, 0xac, 0xe8,
+    0x3b, 0xd1, 0xde, 0xb5, 0x34, 0xaf, 0x8b, 0x2a,
+    0xb0, 0x2d, 0x01, 0xeb, 0x7c, 0xa0, 0x42, 0x26,
+    0xbb, 0x2b, 0x43, 0x0e, 0x1d, 0xe2, 0x4e, 0xc9,
+    0xc1, 0x0a, 0x67, 0x1d, 0xfc, 0x83, 0x25, 0xce,
+    0xb2, 0x18, 0xd9, 0x0d, 0x70, 0xf5, 0xa3, 0x5a,
+    0x9c, 0x99, 0xdd, 0x47, 0xa1, 0x57, 0xe7, 0x20,
+    0xde, 0xa1, 0x29, 0x8d, 0x96, 0x62, 0xf9, 0x26,
+    0x95, 0x51, 0xa6, 0xe7, 0x09, 0x8b, 0xba, 0x16,
+    0x8b, 0x19, 0x5b, 0xf9, 0x27, 0x0d, 0xc5, 0xd6,
+    0x5f, 0x02, 0x81, 0x81, 0x00, 0xce, 0x26, 0x31,
+    0xb5, 0x43, 0x53, 0x95, 0x39, 0xdd, 0x01, 0x98,
+    0x8b, 0x3d, 0x27, 0xeb, 0x0b, 0x87, 0x1c, 0x95,
+    0xfc, 0x3e, 0x36, 0x51, 0x31, 0xb5, 0xea, 0x59,
+    0x56, 0xc0, 0x97, 0x62, 0xf0, 0x63, 0x2b, 0xb6,
+    0x30, 0x9b, 0xdf, 0x19, 0x10, 0xe9, 0xa0, 0x3d,
+    0xea, 0x54, 0x5a, 0xe6, 0xc6, 0x9e, 0x7e, 0xb5,
+    0xf0, 0xb0, 0x54, 0xef, 0xc3, 0xe1, 0x47, 0xa6,
+    0x95, 0xc7, 0xe4, 0xa3, 0x4a, 0x30, 0x68, 0x24,
+    0x98, 0x7d, 0xc1, 0x34, 0xa9, 0xcb, 0xbc, 0x3c,
+    0x08, 0x9c, 0x7d, 0x0c, 0xa2, 0xb7, 0x60, 0xaa,
+    0x38, 0x08, 0x16, 0xa6, 0x7f, 0xdb, 0xd2, 0xb1,
+    0x67, 0xe7, 0x93, 0x8e, 0xbb, 0x7e, 0xb9, 0xb5,
+    0xd0, 0xd0, 0x9f, 0x7b, 0xcc, 0x46, 0xe6, 0x74,
+    0x78, 0x1a, 0x96, 0xd6, 0xd7, 0x74, 0x34, 0x54,
+    0x3b, 0x54, 0x55, 0x7f, 0x89, 0x81, 0xbc, 0x40,
+    0x55, 0x87, 0x24, 0x95, 0x33, 0x02, 0x81, 0x81,
+    0x00, 0xb0, 0x18, 0x5d, 0x2a, 0x1a, 0x95, 0x9f,
+    0x9a, 0xd5, 0x3f, 0x37, 0x79, 0xe6, 0x3d, 0x83,
+    0xab, 0x46, 0x86, 0x36, 0x3a, 0x5d, 0x0c, 0x23,
+    0x73, 0x91, 0x2b, 0xda, 0x63, 0xce, 0x46, 0x68,
+    0xd1, 0xfe, 0x40, 0x90, 0xf2, 0x3e, 0x43, 0x2b,
+    0x19, 0x4c, 0xb1, 0xb0, 0xd5, 0x8c, 0x02, 0x21,
+    0x07, 0x18, 0x17, 0xda, 0xe9, 0x49, 0xd7, 0x82,
+    0x73, 0x42, 0x78, 0xd1, 0x82, 0x4e, 0x8a, 0xc0,
+    0xe9, 0x33, 0x2f, 0xcd, 0x62, 0xce, 0x23, 0xca,
+    0xfd, 0x8d, 0xd4, 0x3f, 0x59, 0x80, 0x27, 0xb6,
+    0x61, 0x85, 0x9b, 0x2a, 0xe4, 0xef, 0x5c, 0x36,
+    0x22, 0x21, 0xcd, 0x2a, 0x6d, 0x41, 0x77, 0xe2,
+    0xcb, 0x5d, 0x93, 0x0d, 0x00, 0x10, 0x52, 0x8d,
+    0xd5, 0x92, 0x28, 0x16, 0x78, 0xd3, 0x1a, 0x4c,
+    0x8d, 0xbd, 0x9c, 0x1a, 0x0b, 0x9c, 0x91, 0x16,
+    0x4c, 0xff, 0x31, 0x36, 0xbb, 0xcb, 0x64, 0x1a,
+    0xf7, 0x02, 0x81, 0x80, 0x32, 0x65, 0x09, 0xdf,
+    0xca, 0xee, 0xa2, 0xdb, 0x3b, 0x58, 0xc9, 0x86,
+    0xb8, 0x53, 0x8a, 0xd5, 0x0d, 0x99, 0x82, 0x5c,
+    0xe0, 0x84, 0x7c, 0xc2, 0xcf, 0x3a, 0xd3, 0xce,
+    0x2e, 0x54, 0x93, 0xbe, 0x3a, 0x30, 0x14, 0x60,
+    0xbb, 0xaa, 0x05, 0x41, 0xaa, 0x2b, 0x1f, 0x17,
+    0xaa, 0xb9, 0x72, 0x12, 0xf9, 0xe9, 0xf5, 0xe6,
+    0x39, 0xe4, 0xf9, 0x9c, 0x03, 0xf5, 0x75, 0x16,
+    0xc6, 0x7f, 0xf1, 0x1f, 0x10, 0xc8, 0x54, 0xb1,
+    0xe6, 0x84, 0x15, 0xb0, 0xb0, 0x7a, 0x7a, 0x9e,
+    0x8c, 0x4a, 0xd1, 0x8c, 0xf1, 0x91, 0x32, 0xeb,
+    0x71, 0xa6, 0xbf, 0xdb, 0x1f, 0xcc, 0xd8, 0xcb,
+    0x92, 0xc3, 0xf2, 0xaf, 0x89, 0x22, 0x32, 0xfd,
+    0x32, 0x12, 0xda, 0xbb, 0xac, 0x55, 0x68, 0x01,
+    0x78, 0x56, 0x89, 0x7c, 0xb0, 0x0e, 0x9e, 0xcc,
+    0xc6, 0x28, 0x04, 0x7e, 0x83, 0xf5, 0x96, 0x30,
+    0x92, 0x51, 0xf2, 0x1b, 0x02, 0x81, 0x81, 0x00,
+    0x83, 0x6d, 0xd1, 0x98, 0x90, 0x41, 0x8c, 0xa7,
+    0x92, 0x83, 0xac, 0x89, 0x05, 0x0c, 0x79, 0x67,
+    0x90, 0xb6, 0xa1, 0xf3, 0x2f, 0xca, 0xf0, 0x15,
+    0xe0, 0x30, 0x58, 0xe9, 0x4f, 0xcb, 0x4c, 0x56,
+    0x56, 0x56, 0x14, 0x3f, 0x1b, 0x79, 0xb6, 0xef,
+    0x57, 0x4b, 0x28, 0xbd, 0xb0, 0xe6, 0x0c, 0x49,
+    0x4b, 0xbe, 0xe1, 0x57, 0x28, 0x2a, 0x23, 0x5e,
+    0xc4, 0xa2, 0x19, 0x4b, 0x00, 0x67, 0x78, 0xd9,
+    0x26, 0x6e, 0x17, 0x25, 0xce, 0xe4, 0xfd, 0xde,
+    0x86, 0xa8, 0x5a, 0x67, 0x47, 0x6b, 0x15, 0x09,
+    0xe1, 0xec, 0x8e, 0x62, 0x98, 0x91, 0x6f, 0xc0,
+    0x98, 0x0c, 0x70, 0x0e, 0x7d, 0xbe, 0x63, 0xbd,
+    0x12, 0x5a, 0x98, 0x1c, 0xe3, 0x0c, 0xfb, 0xc7,
+    0xfb, 0x1b, 0xbd, 0x02, 0x87, 0xcc, 0x0c, 0xbb,
+    0xc2, 0xd4, 0xb6, 0xc1, 0xa1, 0x23, 0xd3, 0x1e,
+    0x21, 0x6f, 0x48, 0xba, 0x0e, 0x2e, 0xc7, 0x42
+};
+
+static const uint8_t rsa4096_priv_key[] = {
+    0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02,
+    0x82, 0x02, 0x01, 0x00, 0xcc, 0x30, 0xc6, 0x90,
+    0x49, 0x2b, 0x86, 0xe7, 0x7a, 0xa5, 0x7a, 0x9a,
+    0x4f, 0xee, 0x0e, 0xa1, 0x5c, 0x43, 0x64, 0xd0,
+    0x76, 0xe1, 0xfd, 0x0b, 0xfd, 0x43, 0x7a, 0x65,
+    0xe6, 0x20, 0xbd, 0xf2, 0x0e, 0xbe, 0x76, 0x54,
+    0xae, 0x37, 0xbe, 0xa0, 0x02, 0x96, 0xae, 0x8d,
+    0x8a, 0xae, 0x3b, 0x88, 0xbb, 0x67, 0xce, 0x7c,
+    0x20, 0xbf, 0x14, 0xc3, 0x71, 0x51, 0x87, 0x03,
+    0x34, 0xaa, 0x3c, 0x09, 0xff, 0xe9, 0xeb, 0xb7,
+    0x85, 0x5c, 0xbb, 0x8d, 0xce, 0x8e, 0x3f, 0xd1,
+    0x16, 0x30, 0x00, 0x32, 0x2f, 0x25, 0x8d, 0xef,
+    0x71, 0xd9, 0xea, 0x6b, 0x45, 0x53, 0x49, 0xc3,
+    0x09, 0x4f, 0xb0, 0xa8, 0xa5, 0x89, 0x76, 0x59,
+    0x31, 0xa5, 0xf1, 0x5c, 0x42, 0x54, 0x57, 0x70,
+    0x57, 0xad, 0xd8, 0xeb, 0x89, 0xa6, 0x87, 0xa2,
+    0x6c, 0x95, 0x58, 0x8f, 0xb6, 0x82, 0xc7, 0xde,
+    0xc2, 0x3a, 0xdc, 0x5b, 0xe8, 0x02, 0xcc, 0x26,
+    0x4b, 0x01, 0xaa, 0xe6, 0xf3, 0x66, 0x4d, 0x90,
+    0x85, 0xde, 0xf4, 0x5d, 0x80, 0x98, 0xc6, 0x65,
+    0xcf, 0x44, 0x4c, 0xde, 0xb5, 0x4a, 0xfc, 0xda,
+    0x0a, 0x0a, 0x10, 0x26, 0xa3, 0xcb, 0x9d, 0xe4,
+    0x8d, 0xab, 0x2c, 0x04, 0xfd, 0xaa, 0xfc, 0x3b,
+    0xac, 0x4e, 0x56, 0xb8, 0x4c, 0x9f, 0x22, 0x49,
+    0xcb, 0x76, 0x45, 0x24, 0x36, 0x2d, 0xbb, 0xe6,
+    0x7e, 0xa9, 0x93, 0x13, 0x96, 0x1e, 0xfc, 0x4b,
+    0x75, 0xd4, 0x54, 0xc8, 0x8c, 0x55, 0xe6, 0x3f,
+    0x09, 0x5a, 0x03, 0x74, 0x7c, 0x8a, 0xc8, 0xe7,
+    0x49, 0x0b, 0x86, 0x7c, 0x97, 0xa0, 0xf2, 0x0d,
+    0xf1, 0x5c, 0x0e, 0x7a, 0xc0, 0x3f, 0x78, 0x2d,
+    0x9b, 0xe2, 0x26, 0xa0, 0x89, 0x49, 0x0c, 0xad,
+    0x79, 0xa6, 0x82, 0x98, 0xa6, 0xb7, 0x74, 0xb4,
+    0x45, 0xc8, 0xed, 0xea, 0x81, 0xcd, 0xf0, 0x3b,
+    0x8e, 0x24, 0xfb, 0x0c, 0xd0, 0x3a, 0x14, 0xb9,
+    0xb4, 0x3b, 0x69, 0xd9, 0xf2, 0x42, 0x6e, 0x7f,
+    0x6f, 0x5e, 0xb1, 0x52, 0x5b, 0xaa, 0xef, 0xae,
+    0x1e, 0x34, 0xca, 0xed, 0x0a, 0x8d, 0x56, 0xd6,
+    0xdd, 0xd4, 0x2c, 0x54, 0x7a, 0x57, 0xca, 0x7e,
+    0x4a, 0x11, 0xde, 0x48, 0xdf, 0x2b, 0x09, 0x97,
+    0x39, 0x24, 0xce, 0x45, 0xe0, 0x75, 0xb1, 0x19,
+    0x42, 0xdb, 0x63, 0x40, 0x9b, 0xb9, 0x95, 0x96,
+    0x78, 0x91, 0xd5, 0x19, 0x12, 0xab, 0xef, 0x55,
+    0x6f, 0x0d, 0x65, 0xc0, 0x8f, 0x62, 0x99, 0x78,
+    0xc0, 0xe0, 0xe1, 0x33, 0xc7, 0x68, 0xff, 0x29,
+    0x66, 0x22, 0x3a, 0x6f, 0xa0, 0xf8, 0x5c, 0x68,
+    0x9b, 0xa9, 0x05, 0xad, 0x6b, 0x1d, 0xae, 0xc1,
+    0x30, 0xbb, 0xfe, 0xb7, 0x31, 0x85, 0x0d, 0xd1,
+    0xd5, 0xfc, 0x43, 0x1e, 0xb3, 0x61, 0x6f, 0xc4,
+    0x75, 0xed, 0x76, 0x9d, 0x13, 0xb3, 0x61, 0x57,
+    0xc8, 0x33, 0x0d, 0x77, 0x84, 0xf0, 0xc7, 0x62,
+    0xb9, 0x9e, 0xd5, 0x01, 0xfa, 0x87, 0x4a, 0xf5,
+    0xd7, 0x4f, 0x5d, 0xae, 0xe7, 0x08, 0xd2, 0x5a,
+    0x65, 0x30, 0xc9, 0xf0, 0x0a, 0x11, 0xf1, 0x2a,
+    0xd3, 0x43, 0x43, 0xca, 0x05, 0x90, 0x85, 0xf4,
+    0xbc, 0x37, 0x49, 0x40, 0x45, 0x35, 0xd3, 0x56,
+    0x06, 0x4c, 0x63, 0x93, 0x07, 0x14, 0x8b, 0xd3,
+    0x12, 0xd0, 0xe5, 0x00, 0x48, 0x76, 0xd2, 0xdf,
+    0x7c, 0xea, 0xc7, 0xff, 0xf0, 0x88, 0xd5, 0xa4,
+    0x61, 0x7d, 0x79, 0xc2, 0xda, 0x53, 0x24, 0xdc,
+    0x20, 0xae, 0xe6, 0x08, 0x65, 0xef, 0xc9, 0x0d,
+    0x7d, 0x66, 0x6d, 0x1b, 0x1c, 0x5d, 0x46, 0xe1,
+    0x26, 0x8a, 0x29, 0x77, 0x76, 0x19, 0xe5, 0x19,
+    0x2a, 0x75, 0x21, 0xf1, 0x92, 0x8a, 0x9c, 0x7b,
+    0xe8, 0x0b, 0x38, 0xc1, 0xbf, 0x76, 0x22, 0x45,
+    0x4a, 0xd3, 0x43, 0xc3, 0x8c, 0x74, 0xd8, 0xd8,
+    0xec, 0x3e, 0x14, 0xdf, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0x02, 0x82, 0x02, 0x01, 0x00, 0x9e, 0x13,
+    0x64, 0xa5, 0x6e, 0xff, 0xf3, 0x80, 0x60, 0xc2,
+    0x9b, 0x17, 0xbb, 0xa9, 0x60, 0x4a, 0x2b, 0x53,
+    0x41, 0x48, 0xe1, 0xc0, 0x32, 0x56, 0x85, 0xcb,
+    0x27, 0x86, 0x9b, 0x91, 0xdd, 0x7a, 0xf7, 0x4f,
+    0x1b, 0xec, 0x92, 0xb3, 0x35, 0x30, 0x4a, 0xd0,
+    0xbc, 0x71, 0x77, 0x5b, 0x4b, 0x5b, 0x9f, 0x39,
+    0xcd, 0xf0, 0xea, 0xa9, 0x03, 0x3a, 0x0b, 0x10,
+    0x42, 0xa5, 0x88, 0xb0, 0x01, 0xaa, 0xfc, 0x23,
+    0xec, 0x08, 0x37, 0x86, 0x82, 0xec, 0x55, 0x6c,
+    0x6a, 0x9b, 0x43, 0xc2, 0x05, 0x64, 0xd4, 0x7b,
+    0x0e, 0x56, 0xc0, 0x9d, 0x23, 0x8d, 0xc8, 0x2d,
+    0xa2, 0x7d, 0x0b, 0x48, 0x56, 0x4b, 0x39, 0x5c,
+    0x21, 0xf3, 0x0b, 0x2c, 0x9c, 0x9d, 0xff, 0xfb,
+    0xab, 0x75, 0x9d, 0x6b, 0x48, 0xf3, 0x8f, 0xad,
+    0x0c, 0x74, 0x01, 0xfb, 0xdc, 0x83, 0xe5, 0x97,
+    0x79, 0x84, 0x4a, 0x79, 0xa6, 0xfe, 0xbf, 0xae,
+    0xea, 0xbc, 0xfa, 0x74, 0x60, 0x0a, 0x4b, 0x84,
+    0x77, 0xa7, 0xda, 0xfb, 0xaf, 0xd2, 0x73, 0x2b,
+    0xd2, 0xec, 0x1e, 0x79, 0x91, 0xc9, 0x18, 0x30,
+    0xe5, 0x6f, 0x27, 0x36, 0x83, 0x2a, 0x66, 0xc3,
+    0xcb, 0x88, 0x94, 0xe4, 0x5f, 0x3f, 0xbd, 0xe2,
+    0x11, 0x43, 0x61, 0x31, 0x84, 0x91, 0x49, 0x40,
+    0x29, 0x1b, 0x58, 0x18, 0x47, 0x8e, 0xb1, 0x22,
+    0xd6, 0xc4, 0xaa, 0x6a, 0x3d, 0x22, 0x7c, 0xa5,
+    0xa0, 0x4c, 0x0a, 0xfc, 0x46, 0x66, 0xbb, 0xbe,
+    0x04, 0x71, 0xe8, 0x9b, 0x76, 0xf1, 0x47, 0x39,
+    0x6a, 0x2f, 0x23, 0xad, 0x78, 0x80, 0x1c, 0x22,
+    0xcd, 0x41, 0x5e, 0x09, 0x16, 0x6c, 0x91, 0x48,
+    0x91, 0x91, 0x3d, 0x8c, 0xe6, 0xba, 0x81, 0x8d,
+    0xbb, 0xf2, 0xd0, 0xaa, 0xc7, 0x8f, 0xc6, 0x01,
+    0x60, 0xa7, 0xef, 0x1e, 0x8e, 0x91, 0x6d, 0xcc,
+    0x30, 0x9e, 0xea, 0x7c, 0x56, 0x9d, 0x42, 0xcf,
+    0x44, 0x85, 0x52, 0xa8, 0xf2, 0x36, 0x9c, 0x46,
+    0xfa, 0x9d, 0xd3, 0x4e, 0x13, 0x46, 0x81, 0xce,
+    0x99, 0xc9, 0x58, 0x47, 0xe4, 0xeb, 0x27, 0x56,
+    0x29, 0x61, 0x0f, 0xb5, 0xcb, 0xf3, 0x48, 0x58,
+    0x8f, 0xbc, 0xaf, 0x0a, 0xbf, 0x40, 0xd1, 0xf6,
+    0x4f, 0xd2, 0x89, 0x4a, 0xff, 0x6f, 0x54, 0x70,
+    0x49, 0x42, 0xf6, 0xf8, 0x0e, 0x4f, 0xa5, 0xf6,
+    0x8b, 0x49, 0x80, 0xd4, 0xf5, 0x03, 0xf8, 0x65,
+    0xe7, 0x1f, 0x0a, 0xc0, 0x8f, 0xd3, 0x7a, 0x70,
+    0xca, 0x67, 0xaf, 0x71, 0xfd, 0x4b, 0xe1, 0x17,
+    0x76, 0x74, 0x2e, 0x12, 0x7b, 0xad, 0x4b, 0xbb,
+    0xd2, 0x64, 0xd0, 0xa9, 0xf9, 0x79, 0xa9, 0xa6,
+    0x03, 0xd2, 0xc2, 0x8f, 0x47, 0x59, 0x1b, 0x7c,
+    0xe3, 0xce, 0x92, 0xb2, 0xac, 0x3e, 0xee, 0x12,
+    0x43, 0x5f, 0x23, 0xec, 0xf1, 0xd3, 0xf2, 0x21,
+    0x22, 0xe8, 0x7e, 0x7f, 0xa4, 0x93, 0x8e, 0x78,
+    0x69, 0x69, 0xa0, 0xc9, 0xce, 0x86, 0x36, 0x13,
+    0x10, 0x21, 0xc4, 0x7a, 0x52, 0xcf, 0x53, 0xd9,
+    0x9b, 0x58, 0xe6, 0x2d, 0xeb, 0x60, 0xe3, 0x75,
+    0x1a, 0x22, 0xf6, 0x3c, 0x54, 0x6b, 0xfa, 0xa1,
+    0x5d, 0xf6, 0x38, 0xf0, 0xd4, 0x26, 0x2d, 0x7d,
+    0x74, 0x99, 0x6a, 0x13, 0x8a, 0x07, 0x9f, 0x07,
+    0xc5, 0xf4, 0xa8, 0x20, 0x11, 0xa9, 0x76, 0x11,
+    0xe4, 0x48, 0xae, 0xa4, 0x8a, 0xa1, 0xbf, 0x1f,
+    0xba, 0x37, 0x50, 0x53, 0x43, 0x91, 0x45, 0x88,
+    0x03, 0x52, 0xba, 0xac, 0xc8, 0xe3, 0xe1, 0xba,
+    0x63, 0x24, 0x72, 0xbe, 0x1d, 0x01, 0x1f, 0x6c,
+    0x34, 0x10, 0xb8, 0x56, 0x4a, 0x67, 0x28, 0x4b,
+    0x7a, 0x2b, 0x31, 0x29, 0x47, 0xda, 0xdf, 0x53,
+    0x88, 0x79, 0x22, 0x31, 0x15, 0x56, 0xe3, 0xa0,
+    0x79, 0x75, 0x94, 0x90, 0xb2, 0xe8, 0x4b, 0xca,
+    0x82, 0x6d, 0x3c, 0x69, 0x43, 0x01, 0x02, 0x82,
+    0x01, 0x01, 0x00, 0xe7, 0x8b, 0xd6, 0x1a, 0xe8,
+    0x00, 0xed, 0x9d, 0x7c, 0x5a, 0x32, 0x10, 0xc1,
+    0x53, 0x50, 0xbe, 0x27, 0x1d, 0xef, 0x69, 0x73,
+    0xa2, 0x8f, 0x95, 0x96, 0x86, 0xfe, 0xfb, 0x82,
+    0xdb, 0xea, 0x7d, 0x73, 0x5a, 0x2b, 0xe7, 0x4b,
+    0xd5, 0x8f, 0x4f, 0xaf, 0x85, 0x1d, 0x15, 0x1a,
+    0x58, 0x5f, 0x41, 0x79, 0x70, 0x5c, 0x8f, 0xa9,
+    0x8e, 0x23, 0x31, 0xa7, 0x6d, 0x99, 0x0c, 0xf0,
+    0x51, 0xbf, 0xbb, 0xd3, 0xe3, 0xa3, 0x34, 0xf0,
+    0x1d, 0x7f, 0x4a, 0xb7, 0x8f, 0xf6, 0x0a, 0x49,
+    0x65, 0xaf, 0x35, 0x7b, 0x02, 0x2e, 0x69, 0x49,
+    0x95, 0xb5, 0x20, 0x70, 0xb2, 0x98, 0x54, 0x9b,
+    0x8e, 0x4f, 0x48, 0xa8, 0xfa, 0x7e, 0xc7, 0x0a,
+    0xae, 0x84, 0xe1, 0xba, 0x85, 0x98, 0x96, 0x8a,
+    0x7c, 0xdd, 0xcc, 0xcd, 0xd8, 0x5b, 0x50, 0x60,
+    0x88, 0x2d, 0xb6, 0x3e, 0xb8, 0xc2, 0xae, 0xa5,
+    0x62, 0x10, 0xcd, 0xdc, 0xae, 0x86, 0xfe, 0x31,
+    0x8b, 0xf7, 0xee, 0x1a, 0x35, 0x46, 0x83, 0xee,
+    0x5f, 0x55, 0x9a, 0xc2, 0xca, 0x53, 0xb7, 0x2c,
+    0xbf, 0x03, 0x8a, 0x78, 0xcc, 0x1d, 0x96, 0x7b,
+    0xac, 0x00, 0x62, 0x1e, 0xbd, 0x6f, 0x0b, 0xa5,
+    0xec, 0xf3, 0x02, 0x47, 0x47, 0x1e, 0x3d, 0xf6,
+    0x78, 0x42, 0xe4, 0xcd, 0xf8, 0x14, 0xa3, 0x7d,
+    0xd5, 0x2f, 0x6e, 0xcc, 0x1a, 0x9e, 0xe7, 0xcf,
+    0x48, 0xb9, 0x80, 0xb8, 0xba, 0xaa, 0x7b, 0xae,
+    0x65, 0x74, 0x09, 0x7b, 0x43, 0x26, 0x31, 0xa2,
+    0x95, 0x43, 0x69, 0xd0, 0xb7, 0x95, 0xe4, 0x76,
+    0x2c, 0x42, 0x19, 0x47, 0x4f, 0x63, 0x35, 0x9c,
+    0xa2, 0x1a, 0xce, 0x28, 0xdf, 0x76, 0x98, 0x1d,
+    0xd4, 0x2e, 0xf6, 0x3a, 0xc8, 0x3e, 0xc7, 0xaf,
+    0xf7, 0x38, 0x3f, 0x83, 0x3a, 0xcb, 0xae, 0x41,
+    0x75, 0x46, 0x63, 0xaa, 0x45, 0xb1, 0x2c, 0xd9,
+    0x9f, 0x17, 0x37, 0x02, 0x82, 0x01, 0x01, 0x00,
+    0xe1, 0xc1, 0x57, 0x4d, 0x0f, 0xa5, 0xea, 0x1d,
+    0x39, 0x9c, 0xe0, 0xf0, 0x6d, 0x13, 0x7f, 0x79,
+    0xdc, 0x72, 0x61, 0xc0, 0x7f, 0x88, 0xf6, 0x38,
+    0x4f, 0x49, 0x06, 0x1e, 0xb8, 0x6c, 0x21, 0x04,
+    0x60, 0x76, 0x5a, 0x6d, 0x04, 0xd1, 0x6d, 0xac,
+    0x7c, 0x25, 0x4f, 0x32, 0xcb, 0xbc, 0xf8, 0x4a,
+    0x22, 0x8f, 0xf5, 0x41, 0xfd, 0x1c, 0x76, 0x30,
+    0xc2, 0x5f, 0x99, 0x13, 0x5c, 0x57, 0x0f, 0xfd,
+    0xac, 0x0b, 0x10, 0x9a, 0x4f, 0x78, 0x0a, 0x86,
+    0xe8, 0x07, 0x40, 0x40, 0x13, 0xba, 0x96, 0x07,
+    0xd5, 0x39, 0x91, 0x51, 0x3e, 0x80, 0xd8, 0xa0,
+    0x1f, 0xff, 0xdc, 0x9e, 0x09, 0x3b, 0xae, 0x38,
+    0xa9, 0xc2, 0x14, 0x7b, 0xee, 0xd2, 0x69, 0x3d,
+    0xd6, 0x26, 0x74, 0x72, 0x7b, 0x86, 0xd4, 0x13,
+    0x5b, 0xb8, 0x76, 0x4b, 0x08, 0xfb, 0x93, 0xfa,
+    0x44, 0xaf, 0x98, 0x3b, 0xfa, 0xd0, 0x2a, 0x04,
+    0x8b, 0xb3, 0x3c, 0x6d, 0x32, 0xf7, 0x18, 0x6a,
+    0x51, 0x0e, 0x40, 0x90, 0xce, 0x8e, 0xdf, 0xe8,
+    0x07, 0x4c, 0x0f, 0xc7, 0xc8, 0xc2, 0x18, 0x58,
+    0x6a, 0x01, 0xc8, 0x27, 0xd6, 0x43, 0x2a, 0xfb,
+    0xa5, 0x34, 0x01, 0x3c, 0x72, 0xb1, 0x48, 0xce,
+    0x2b, 0x9b, 0xb4, 0x69, 0xd9, 0x82, 0xf8, 0xbe,
+    0x29, 0x88, 0x75, 0x96, 0xd8, 0xef, 0x78, 0x2a,
+    0x07, 0x90, 0xa0, 0x56, 0x33, 0x42, 0x05, 0x19,
+    0xb0, 0x69, 0x34, 0xf9, 0x03, 0xc5, 0xa8, 0x0d,
+    0x72, 0xa2, 0x27, 0xb4, 0x45, 0x6d, 0xd2, 0x01,
+    0x6c, 0xf1, 0x74, 0x51, 0x0a, 0x9a, 0xe2, 0xc1,
+    0x96, 0x80, 0x30, 0x0e, 0xc6, 0xa9, 0x79, 0xf7,
+    0x6f, 0xaf, 0xf6, 0xe8, 0x2a, 0xcc, 0xbd, 0xad,
+    0x8f, 0xe0, 0x32, 0x87, 0x85, 0x49, 0x68, 0x88,
+    0x15, 0x5c, 0xdb, 0x48, 0x40, 0xa2, 0xfa, 0x42,
+    0xe8, 0x4e, 0x3e, 0xe2, 0x3f, 0xe0, 0xf3, 0x99,
+    0x02, 0x82, 0x01, 0x00, 0x08, 0x39, 0x97, 0x69,
+    0x6d, 0x44, 0x5b, 0x2c, 0x74, 0xf6, 0x5f, 0x40,
+    0xe9, 0x1d, 0x24, 0x89, 0x1c, 0xaa, 0x9b, 0x8e,
+    0x8b, 0x65, 0x02, 0xe4, 0xb5, 0x6c, 0x26, 0x32,
+    0x98, 0xfb, 0x66, 0xe0, 0xfd, 0xef, 0xfe, 0x0f,
+    0x41, 0x4a, 0x5c, 0xc4, 0xdf, 0xdf, 0x42, 0xa1,
+    0x35, 0x46, 0x5e, 0x5b, 0xdd, 0x0c, 0x78, 0xbd,
+    0x41, 0xb0, 0xa2, 0xdf, 0x68, 0xab, 0x23, 0xfc,
+    0xa9, 0xac, 0xbd, 0xba, 0xd6, 0x54, 0x07, 0xc0,
+    0x21, 0xa7, 0x6a, 0x96, 0x24, 0xdf, 0x20, 0x46,
+    0x4d, 0x45, 0x27, 0x6c, 0x26, 0xea, 0x74, 0xeb,
+    0x98, 0x89, 0x90, 0xdd, 0x8e, 0x23, 0x49, 0xf5,
+    0xf7, 0x70, 0x9e, 0xb0, 0x5e, 0x10, 0x47, 0xe0,
+    0x9a, 0x28, 0x88, 0xdf, 0xdb, 0xd8, 0x53, 0x0b,
+    0x45, 0xf0, 0x19, 0x90, 0xe4, 0xdf, 0x02, 0x9f,
+    0x60, 0x4e, 0x76, 0x11, 0x3b, 0x39, 0x24, 0xf1,
+    0x3f, 0x3e, 0xb4, 0x8a, 0x1b, 0x84, 0xb7, 0x96,
+    0xdf, 0xfb, 0xb0, 0xda, 0xec, 0x63, 0x68, 0x15,
+    0xd7, 0xa9, 0xdb, 0x48, 0x9c, 0x12, 0xc3, 0xd6,
+    0x85, 0xe8, 0x63, 0x1f, 0xd0, 0x1a, 0xb0, 0x12,
+    0x60, 0x62, 0x43, 0xc1, 0x38, 0x86, 0x52, 0x23,
+    0x7f, 0xc9, 0x62, 0xf8, 0x79, 0xbf, 0xb4, 0xfb,
+    0x4e, 0x7e, 0x07, 0x22, 0x49, 0x8e, 0xbe, 0x6c,
+    0xf0, 0x53, 0x5a, 0x53, 0xfd, 0x3c, 0x14, 0xd8,
+    0xf7, 0x2c, 0x06, 0x2a, 0xe4, 0x64, 0xfd, 0x19,
+    0x57, 0xa0, 0x92, 0xf6, 0xa3, 0x42, 0x47, 0x61,
+    0x0b, 0xfd, 0x71, 0x5f, 0x98, 0xe2, 0x6c, 0x98,
+    0xa8, 0xf9, 0xf9, 0x7f, 0x1c, 0x61, 0x5d, 0x8c,
+    0xd1, 0xfb, 0x90, 0x28, 0x32, 0x9b, 0x7d, 0x82,
+    0xf9, 0xcc, 0x47, 0xbe, 0xc7, 0x67, 0xc5, 0x93,
+    0x22, 0x55, 0x0d, 0xd2, 0x73, 0xbe, 0xea, 0xed,
+    0x4d, 0xb5, 0xf4, 0xc2, 0x25, 0x92, 0x44, 0x30,
+    0xeb, 0xaa, 0x13, 0x11, 0x02, 0x82, 0x01, 0x01,
+    0x00, 0x82, 0x42, 0x02, 0x53, 0x4e, 0x72, 0x16,
+    0xf1, 0x21, 0xea, 0xe8, 0xc7, 0x10, 0xc8, 0xad,
+    0x46, 0xec, 0xf1, 0x7a, 0x81, 0x8d, 0x94, 0xc3,
+    0x2c, 0x9e, 0x62, 0xae, 0x0b, 0x4f, 0xb1, 0xe4,
+    0x23, 0x18, 0x5d, 0x71, 0xb3, 0x71, 0x92, 0x3d,
+    0x4b, 0xc6, 0x9d, 0xe8, 0x62, 0x90, 0xb7, 0xca,
+    0x33, 0x4c, 0x59, 0xef, 0xd3, 0x51, 0x6d, 0xf8,
+    0xac, 0x0d, 0x9b, 0x07, 0x41, 0xea, 0x87, 0xb9,
+    0x8c, 0x4e, 0x96, 0x5b, 0xd0, 0x0d, 0x86, 0x5f,
+    0xdc, 0x93, 0x48, 0x8b, 0xc3, 0xed, 0x1e, 0x3d,
+    0xae, 0xeb, 0x52, 0xba, 0x0c, 0x3c, 0x9a, 0x2f,
+    0x63, 0xc4, 0xd2, 0xe6, 0xc2, 0xb0, 0xe5, 0x24,
+    0x93, 0x41, 0x2f, 0xe0, 0x8d, 0xd9, 0xb0, 0xc2,
+    0x54, 0x91, 0x99, 0xc2, 0x9a, 0xc3, 0xb7, 0x79,
+    0xea, 0x69, 0x83, 0xb7, 0x8d, 0x77, 0xf3, 0x60,
+    0xe0, 0x88, 0x7d, 0x20, 0xc3, 0x8a, 0xe6, 0x4d,
+    0x38, 0x2e, 0x3b, 0x0e, 0xe4, 0x9b, 0x01, 0x83,
+    0xae, 0xe4, 0x71, 0xea, 0xc3, 0x22, 0xcb, 0xc1,
+    0x59, 0xa9, 0xcc, 0x33, 0x56, 0xbc, 0xf9, 0x70,
+    0xfe, 0xa2, 0xbb, 0xc0, 0x77, 0x6b, 0xe3, 0x79,
+    0x8b, 0x95, 0x38, 0xba, 0x75, 0xdc, 0x5f, 0x7a,
+    0x78, 0xab, 0x24, 0xbe, 0x26, 0x4d, 0x00, 0x8a,
+    0xf1, 0x7e, 0x19, 0x64, 0x6f, 0xd3, 0x5f, 0xe8,
+    0xdf, 0xa7, 0x59, 0xc5, 0x89, 0xb7, 0x2d, 0xa2,
+    0xaf, 0xbd, 0xe0, 0x16, 0x56, 0x8f, 0xdc, 0x9e,
+    0x28, 0x94, 0x3a, 0x07, 0xda, 0xb6, 0x2c, 0xb5,
+    0x7d, 0x69, 0x14, 0xb0, 0x5e, 0x8a, 0x55, 0xef,
+    0xfc, 0x6f, 0x10, 0x2b, 0xaa, 0x7a, 0xea, 0x12,
+    0x9b, 0xb8, 0x6f, 0xb9, 0x71, 0x20, 0x30, 0xde,
+    0x48, 0xa4, 0xb9, 0x61, 0xae, 0x5c, 0x33, 0x8d,
+    0x02, 0xe8, 0x00, 0x99, 0xed, 0xc8, 0x8d, 0xc1,
+    0x04, 0x95, 0xf1, 0x7f, 0xcb, 0x1f, 0xbc, 0x76,
+    0x11, 0x02, 0x82, 0x01, 0x00, 0x2d, 0x0c, 0xa9,
+    0x8f, 0x11, 0xc2, 0xf3, 0x02, 0xc8, 0xf2, 0x55,
+    0xc5, 0x6d, 0x25, 0x88, 0xba, 0x59, 0xf6, 0xd1,
+    0xdb, 0x94, 0x2f, 0x0b, 0x65, 0x2c, 0xad, 0x54,
+    0xe0, 0x2b, 0xe6, 0xa3, 0x49, 0xa2, 0xb3, 0xca,
+    0xd7, 0xec, 0x27, 0x32, 0xbb, 0xa4, 0x16, 0x90,
+    0xbb, 0x67, 0xad, 0x1b, 0xb9, 0x0f, 0x78, 0xcb,
+    0xad, 0x5c, 0xc3, 0x66, 0xd6, 0xbb, 0x97, 0x28,
+    0x01, 0x31, 0xf9, 0x0f, 0x71, 0x2a, 0xb9, 0x5b,
+    0xea, 0x34, 0x49, 0x9c, 0x6b, 0x13, 0x40, 0x65,
+    0xbd, 0x18, 0x0a, 0x14, 0xf9, 0x33, 0x47, 0xe8,
+    0x9f, 0x64, 0x0e, 0x24, 0xf6, 0xbb, 0x90, 0x23,
+    0x66, 0x01, 0xa6, 0xa4, 0xa9, 0x7f, 0x64, 0x51,
+    0xa3, 0x8a, 0x73, 0xc1, 0x80, 0xaf, 0x7a, 0x49,
+    0x75, 0x5d, 0x56, 0x1c, 0xaa, 0x3f, 0x64, 0xa9,
+    0x96, 0xfd, 0xb0, 0x90, 0xc5, 0xe0, 0x3d, 0x36,
+    0x05, 0xad, 0xad, 0x84, 0x93, 0x84, 0xab, 0x1b,
+    0x34, 0x57, 0x39, 0xae, 0x0e, 0x80, 0x0f, 0x4a,
+    0x9b, 0x32, 0x56, 0xbd, 0x30, 0xeb, 0xd1, 0xc8,
+    0xc4, 0x9f, 0x9c, 0x07, 0xb6, 0x05, 0xb1, 0x21,
+    0x7f, 0x69, 0x92, 0x9f, 0xb7, 0x68, 0xe7, 0xde,
+    0xb7, 0xbc, 0xb4, 0x89, 0x5b, 0x1c, 0x1b, 0x48,
+    0xd1, 0x44, 0x6e, 0xd7, 0x6b, 0xe2, 0xa1, 0xf4,
+    0xbf, 0x17, 0xb4, 0x43, 0x70, 0x26, 0xd4, 0xb9,
+    0xf5, 0x19, 0x09, 0x08, 0xe9, 0xa3, 0x49, 0x7d,
+    0x2f, 0xdc, 0xe8, 0x75, 0x79, 0xa1, 0xc1, 0x70,
+    0x1b, 0x60, 0x97, 0xaf, 0x0c, 0x56, 0x68, 0xac,
+    0x0e, 0x53, 0xbe, 0x56, 0xf4, 0xc3, 0xb1, 0xfb,
+    0xfb, 0xff, 0x73, 0x5b, 0xa7, 0xf6, 0x99, 0x0e,
+    0x14, 0x5a, 0x5f, 0x9d, 0xbd, 0x8e, 0x94, 0xec,
+    0x8b, 0x38, 0x72, 0xbc, 0x8b, 0xca, 0x32, 0xa8,
+    0x39, 0x43, 0xb1, 0x1d, 0x43, 0x29, 0xbe, 0x60,
+    0xdb, 0x91, 0x6c, 0x9c, 0x06,
+};
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index a82603d7cb..8db9ef4677 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -78,6 +78,7 @@ if have_block
     'test-crypto-hash': [crypto],
     'test-crypto-hmac': [crypto],
     'test-crypto-cipher': [crypto],
+    'test-crypto-akcipher': [crypto],
     'test-crypto-secret': [crypto, keyutils],
     'test-authz-simple': [authz],
     'test-authz-list': [authz],
diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c
new file mode 100644
index 0000000000..b5be563884
--- /dev/null
+++ b/tests/unit/test-crypto-akcipher.c
@@ -0,0 +1,711 @@
+/*
+ * QEMU Crypto cipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "crypto/init.h"
+#include "crypto/akcipher.h"
+#include "qapi/error.h"
+
+static const uint8_t rsa1024_private_key[] = {
+    0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02,
+    0x81, 0x81, 0x00, 0xe6, 0x4d, 0x76, 0x4f, 0xb2,
+    0x97, 0x09, 0xad, 0x9d, 0x17, 0x33, 0xf2, 0x30,
+    0x42, 0x83, 0xa9, 0xcb, 0x49, 0xa4, 0x2e, 0x59,
+    0x5e, 0x75, 0x51, 0xd1, 0xac, 0xc8, 0x86, 0x3e,
+    0xdb, 0x72, 0x2e, 0xb2, 0xf7, 0xc3, 0x5b, 0xc7,
+    0xea, 0xed, 0x30, 0xd1, 0xf7, 0x37, 0xee, 0x9d,
+    0x36, 0x59, 0x6f, 0xf8, 0xce, 0xc0, 0x5c, 0x82,
+    0x80, 0x37, 0x83, 0xd7, 0x45, 0x6a, 0xe9, 0xea,
+    0xc5, 0x3a, 0x59, 0x6b, 0x34, 0x31, 0x44, 0x00,
+    0x74, 0xa7, 0x29, 0xab, 0x79, 0x4a, 0xbd, 0xe8,
+    0x25, 0x35, 0x01, 0x11, 0x40, 0xbf, 0x31, 0xbd,
+    0xd3, 0xe0, 0x68, 0x1e, 0xd5, 0x5b, 0x2f, 0xe9,
+    0x20, 0xf2, 0x9f, 0x46, 0x35, 0x30, 0xa8, 0xf1,
+    0xfe, 0xef, 0xd8, 0x76, 0x23, 0x46, 0x34, 0x70,
+    0xa1, 0xce, 0xc6, 0x65, 0x6d, 0xb0, 0x94, 0x7e,
+    0xe5, 0x92, 0x45, 0x7b, 0xaa, 0xbb, 0x95, 0x97,
+    0x77, 0xcd, 0xd3, 0x02, 0x03, 0x01, 0x00, 0x01,
+    0x02, 0x81, 0x80, 0x30, 0x6a, 0xc4, 0x9e, 0xc8,
+    0xba, 0xfc, 0x2b, 0xe5, 0xc4, 0xc5, 0x04, 0xfb,
+    0xa4, 0x60, 0x2d, 0xc8, 0x31, 0x39, 0x35, 0x0d,
+    0x50, 0xd0, 0x75, 0x5d, 0x11, 0x68, 0x2e, 0xe0,
+    0xf4, 0x1d, 0xb3, 0x37, 0xa8, 0xe3, 0x07, 0x5e,
+    0xa6, 0x43, 0x2b, 0x6a, 0x59, 0x01, 0x07, 0x47,
+    0x41, 0xef, 0xd7, 0x9c, 0x85, 0x4a, 0xe7, 0xa7,
+    0xff, 0xf0, 0xab, 0xe5, 0x0c, 0x11, 0x08, 0x10,
+    0x75, 0x5a, 0x68, 0xa0, 0x08, 0x03, 0xc9, 0x40,
+    0x79, 0x67, 0x1d, 0x65, 0x89, 0x2d, 0x08, 0xf9,
+    0xb5, 0x1b, 0x7d, 0xd2, 0x41, 0x3b, 0x33, 0xf2,
+    0x47, 0x2f, 0x9c, 0x0b, 0xd5, 0xaf, 0xcb, 0xdb,
+    0xbb, 0x37, 0x63, 0x03, 0xf8, 0xe7, 0x2e, 0xc7,
+    0x3c, 0x86, 0x9f, 0xc2, 0x9b, 0xb4, 0x70, 0x6a,
+    0x4d, 0x7c, 0xe4, 0x1b, 0x3a, 0xa9, 0xae, 0xd7,
+    0xce, 0x7f, 0x56, 0xc2, 0x73, 0x5e, 0x58, 0x63,
+    0xd5, 0x86, 0x41, 0x02, 0x41, 0x00, 0xf6, 0x56,
+    0x69, 0xec, 0xef, 0x65, 0x95, 0xdc, 0x25, 0x47,
+    0xe0, 0x6f, 0xb0, 0x4f, 0x79, 0x77, 0x0a, 0x5e,
+    0x46, 0xcb, 0xbd, 0x0b, 0x71, 0x51, 0x2a, 0xa4,
+    0x65, 0x29, 0x18, 0xc6, 0x30, 0xa0, 0x95, 0x4c,
+    0x4b, 0xbe, 0x8c, 0x40, 0xe3, 0x9c, 0x23, 0x02,
+    0x14, 0x43, 0xe9, 0x64, 0xea, 0xe3, 0xa8, 0xe2,
+    0x1a, 0xd5, 0xf9, 0x5c, 0xe0, 0x36, 0x2c, 0x97,
+    0xda, 0xd5, 0xc7, 0x46, 0xce, 0x11, 0x02, 0x41,
+    0x00, 0xef, 0x56, 0x08, 0xb8, 0x29, 0xa5, 0xa6,
+    0x7c, 0xf7, 0x5f, 0xb4, 0xf5, 0x63, 0xe7, 0xeb,
+    0x45, 0xfd, 0x89, 0xaa, 0x94, 0xa6, 0x3d, 0x0b,
+    0xd9, 0x04, 0x6f, 0x78, 0xe0, 0xbb, 0xa2, 0xd4,
+    0x29, 0x83, 0x17, 0x95, 0x6f, 0x50, 0x3d, 0x40,
+    0x5d, 0xe5, 0x24, 0xda, 0xc2, 0x23, 0x50, 0x86,
+    0xa8, 0x34, 0xc8, 0x6f, 0xec, 0x7f, 0xb6, 0x45,
+    0x3a, 0xdd, 0x78, 0x9b, 0xee, 0xa1, 0xe4, 0x09,
+    0xa3, 0x02, 0x40, 0x5c, 0xd6, 0x66, 0x67, 0x58,
+    0x35, 0xc5, 0xcb, 0xc8, 0xf5, 0x14, 0xbd, 0xa3,
+    0x09, 0xe0, 0xb2, 0x1f, 0x63, 0x36, 0x75, 0x34,
+    0x52, 0xea, 0xaa, 0xf7, 0x52, 0x2b, 0x99, 0xd8,
+    0x6f, 0x61, 0x06, 0x34, 0x1e, 0x23, 0xf1, 0xb5,
+    0x34, 0x03, 0x53, 0xe5, 0xd1, 0xb3, 0xc7, 0x80,
+    0x5f, 0x7b, 0x32, 0xbf, 0x84, 0x2f, 0x2e, 0xf3,
+    0x22, 0xb0, 0x91, 0x5a, 0x2f, 0x04, 0xd7, 0x4a,
+    0x9a, 0x01, 0xb1, 0x02, 0x40, 0x34, 0x0b, 0x26,
+    0x4c, 0x3d, 0xaa, 0x2a, 0xc0, 0xe3, 0xdd, 0xe8,
+    0xf0, 0xaf, 0x6f, 0xe0, 0x06, 0x51, 0x32, 0x9d,
+    0x68, 0x43, 0x99, 0xe4, 0xb8, 0xa5, 0x31, 0x44,
+    0x3c, 0xc2, 0x30, 0x8f, 0x28, 0x13, 0xbc, 0x8e,
+    0x1f, 0x2d, 0x78, 0x94, 0x45, 0x96, 0xad, 0x63,
+    0xf0, 0x71, 0x53, 0x72, 0x64, 0xa3, 0x4d, 0xae,
+    0xa0, 0xe3, 0xc8, 0x93, 0xd7, 0x50, 0x0f, 0x89,
+    0x00, 0xe4, 0x2d, 0x3d, 0x37, 0x02, 0x41, 0x00,
+    0xbe, 0xa6, 0x08, 0xe0, 0xc8, 0x15, 0x2a, 0x47,
+    0xcb, 0xd5, 0xec, 0x93, 0xd3, 0xaa, 0x12, 0x82,
+    0xaf, 0xac, 0x51, 0x5a, 0x5b, 0xa7, 0x93, 0x4b,
+    0xb9, 0xab, 0x00, 0xfa, 0x5a, 0xea, 0x34, 0xe4,
+    0x80, 0xf1, 0x44, 0x6a, 0x65, 0xe4, 0x33, 0x99,
+    0xfb, 0x54, 0xd7, 0x89, 0x5a, 0x1b, 0xd6, 0x2b,
+    0xcc, 0x6e, 0x4b, 0x19, 0xa0, 0x6d, 0x93, 0x9f,
+    0xc3, 0x91, 0x7a, 0xa5, 0xd8, 0x59, 0x0e, 0x9e,
+};
+
+static const uint8_t rsa1024_public_key[] = {
+    0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xe6,
+    0x4d, 0x76, 0x4f, 0xb2, 0x97, 0x09, 0xad, 0x9d,
+    0x17, 0x33, 0xf2, 0x30, 0x42, 0x83, 0xa9, 0xcb,
+    0x49, 0xa4, 0x2e, 0x59, 0x5e, 0x75, 0x51, 0xd1,
+    0xac, 0xc8, 0x86, 0x3e, 0xdb, 0x72, 0x2e, 0xb2,
+    0xf7, 0xc3, 0x5b, 0xc7, 0xea, 0xed, 0x30, 0xd1,
+    0xf7, 0x37, 0xee, 0x9d, 0x36, 0x59, 0x6f, 0xf8,
+    0xce, 0xc0, 0x5c, 0x82, 0x80, 0x37, 0x83, 0xd7,
+    0x45, 0x6a, 0xe9, 0xea, 0xc5, 0x3a, 0x59, 0x6b,
+    0x34, 0x31, 0x44, 0x00, 0x74, 0xa7, 0x29, 0xab,
+    0x79, 0x4a, 0xbd, 0xe8, 0x25, 0x35, 0x01, 0x11,
+    0x40, 0xbf, 0x31, 0xbd, 0xd3, 0xe0, 0x68, 0x1e,
+    0xd5, 0x5b, 0x2f, 0xe9, 0x20, 0xf2, 0x9f, 0x46,
+    0x35, 0x30, 0xa8, 0xf1, 0xfe, 0xef, 0xd8, 0x76,
+    0x23, 0x46, 0x34, 0x70, 0xa1, 0xce, 0xc6, 0x65,
+    0x6d, 0xb0, 0x94, 0x7e, 0xe5, 0x92, 0x45, 0x7b,
+    0xaa, 0xbb, 0x95, 0x97, 0x77, 0xcd, 0xd3, 0x02,
+    0x03, 0x01, 0x00, 0x01,
+};
+
+static const uint8_t rsa2048_private_key[] = {
+    0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02,
+    0x82, 0x01, 0x01, 0x00, 0xbd, 0x9c, 0x83, 0x6b,
+    0x0e, 0x8e, 0xcf, 0xfa, 0xaa, 0x4f, 0x6a, 0xf4,
+    0xe3, 0x52, 0x0f, 0xa5, 0xd0, 0xbe, 0x5e, 0x7f,
+    0x08, 0x24, 0xba, 0x87, 0x46, 0xfb, 0x28, 0x93,
+    0xe5, 0xe5, 0x81, 0x42, 0xc0, 0xf9, 0x17, 0xc7,
+    0x81, 0x01, 0xf4, 0x18, 0x6a, 0x17, 0xf5, 0x57,
+    0x20, 0x37, 0xcf, 0xf9, 0x74, 0x5e, 0xe1, 0x48,
+    0x6a, 0x71, 0x0a, 0x0f, 0x79, 0x72, 0x2b, 0x46,
+    0x10, 0x53, 0xdc, 0x14, 0x43, 0xbd, 0xbc, 0x6d,
+    0x15, 0x6f, 0x15, 0x4e, 0xf0, 0x0d, 0x89, 0x39,
+    0x02, 0xc3, 0x68, 0x5c, 0xa8, 0xfc, 0xed, 0x64,
+    0x9d, 0x98, 0xb7, 0xcd, 0x83, 0x66, 0x93, 0xc3,
+    0xd9, 0x57, 0xa0, 0x21, 0x93, 0xad, 0x5c, 0x75,
+    0x69, 0x88, 0x9e, 0x81, 0xdc, 0x7f, 0x1d, 0xd5,
+    0xbd, 0x1c, 0xc1, 0x30, 0x56, 0xa5, 0xda, 0x99,
+    0x46, 0xa6, 0x6d, 0x0e, 0x6f, 0x5e, 0x51, 0x34,
+    0x49, 0x73, 0xc3, 0x67, 0x49, 0x7e, 0x21, 0x2a,
+    0x20, 0xa7, 0x2b, 0x92, 0x73, 0x1d, 0xa5, 0x25,
+    0x2a, 0xd0, 0x3a, 0x89, 0x75, 0xb2, 0xbb, 0x19,
+    0x37, 0x78, 0x48, 0xd2, 0xf2, 0x2a, 0x6d, 0x9e,
+    0xc6, 0x26, 0xca, 0x46, 0x8c, 0xf1, 0x42, 0x2a,
+    0x31, 0xb2, 0xfc, 0xe7, 0x55, 0x51, 0xff, 0x07,
+    0x13, 0x5b, 0x36, 0x59, 0x2b, 0x43, 0x30, 0x4b,
+    0x05, 0x5c, 0xd2, 0x45, 0xa0, 0xa0, 0x7c, 0x17,
+    0x5b, 0x07, 0xbb, 0x5d, 0x83, 0x80, 0x92, 0x6d,
+    0x87, 0x1a, 0x43, 0xac, 0xc7, 0x6b, 0x8d, 0x11,
+    0x60, 0x27, 0xd2, 0xdf, 0xdb, 0x71, 0x02, 0x55,
+    0x6e, 0xb5, 0xca, 0x4d, 0xda, 0x59, 0x0d, 0xb8,
+    0x8c, 0xcd, 0xd3, 0x0e, 0x55, 0xa0, 0xa4, 0x8d,
+    0xa0, 0x14, 0x10, 0x48, 0x42, 0x35, 0x56, 0x08,
+    0xf7, 0x29, 0x5f, 0xa2, 0xea, 0xa4, 0x5e, 0x8e,
+    0x99, 0x56, 0xaa, 0x5a, 0x8c, 0x23, 0x8f, 0x35,
+    0x22, 0x8a, 0xff, 0xed, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0x02, 0x82, 0x01, 0x00, 0x4e, 0x4a, 0xf3,
+    0x44, 0xe0, 0x64, 0xfd, 0xe1, 0xde, 0x33, 0x1e,
+    0xd1, 0xf1, 0x8f, 0x6f, 0xe0, 0xa2, 0xfa, 0x08,
+    0x60, 0xe1, 0xc6, 0xf0, 0xb2, 0x6d, 0x0f, 0xc6,
+    0x28, 0x93, 0xb4, 0x19, 0x94, 0xab, 0xc3, 0xef,
+    0x1a, 0xb4, 0xdd, 0x4e, 0xa2, 0x4a, 0x24, 0x8c,
+    0x6c, 0xa6, 0x64, 0x05, 0x5f, 0x56, 0xba, 0xda,
+    0xc1, 0x21, 0x1a, 0x7d, 0xf1, 0xf7, 0xce, 0xb9,
+    0xa9, 0x9b, 0x92, 0x54, 0xfc, 0x95, 0x20, 0x22,
+    0x4e, 0xd4, 0x9b, 0xe2, 0xab, 0x8e, 0x99, 0xb8,
+    0x40, 0xaf, 0x30, 0x6a, 0xc6, 0x60, 0x0c, 0xd8,
+    0x25, 0x44, 0xa1, 0xcb, 0xbb, 0x73, 0x77, 0x86,
+    0xaa, 0x46, 0xf3, 0x54, 0xae, 0xa8, 0xa0, 0xdb,
+    0xdd, 0xab, 0x6e, 0xfb, 0x2c, 0x5a, 0x14, 0xaf,
+    0x08, 0x13, 0xa7, 0x6c, 0xe9, 0xfd, 0xcd, 0x4c,
+    0x1f, 0x20, 0x3a, 0x16, 0x2b, 0xf0, 0xb6, 0x7c,
+    0x47, 0x5f, 0xd1, 0x0a, 0x2c, 0xc4, 0xa5, 0x68,
+    0xd0, 0x43, 0x75, 0x6b, 0x65, 0xaa, 0x32, 0xc6,
+    0x99, 0x06, 0xcb, 0x8f, 0xe6, 0x8d, 0xce, 0xbf,
+    0x4d, 0x0d, 0x7b, 0x22, 0x2a, 0x8a, 0xcb, 0x7d,
+    0x7f, 0x16, 0x48, 0x85, 0xf1, 0x86, 0xcb, 0x54,
+    0xb9, 0x39, 0xd4, 0xbc, 0xe3, 0x2d, 0x27, 0x59,
+    0xf6, 0x81, 0x5e, 0x94, 0x45, 0xdf, 0xb9, 0x22,
+    0xaf, 0x64, 0x0d, 0x14, 0xec, 0x8c, 0xeb, 0x71,
+    0xac, 0xee, 0x09, 0x4c, 0xbf, 0x34, 0xf9, 0xf4,
+    0x66, 0x77, 0x36, 0x3b, 0x41, 0x74, 0x01, 0x4f,
+    0xfc, 0x56, 0x83, 0xba, 0x14, 0xb0, 0x2f, 0xdd,
+    0x4d, 0xb9, 0x3f, 0xdf, 0x71, 0xbe, 0x7b, 0xba,
+    0x66, 0xc8, 0xc5, 0x42, 0xc9, 0xba, 0x18, 0x63,
+    0x45, 0x07, 0x2f, 0x84, 0x3e, 0xc3, 0xfb, 0x47,
+    0xda, 0xd4, 0x1d, 0x0e, 0x9d, 0x96, 0xc0, 0xea,
+    0xee, 0x45, 0x2f, 0xe1, 0x62, 0x23, 0xee, 0xef,
+    0x3d, 0x5e, 0x55, 0xa1, 0x0d, 0x02, 0x81, 0x81,
+    0x00, 0xeb, 0x76, 0x88, 0xd3, 0xae, 0x3f, 0x1d,
+    0xf2, 0x49, 0xe0, 0x37, 0x49, 0x83, 0x82, 0x6c,
+    0xf7, 0xf1, 0x17, 0x30, 0x75, 0x2e, 0x89, 0x06,
+    0x88, 0x56, 0x32, 0xf6, 0xfa, 0x58, 0xcb, 0x3c,
+    0x98, 0x67, 0xc3, 0xde, 0x10, 0x82, 0xe5, 0xfa,
+    0xfa, 0x52, 0x47, 0x8d, 0xd7, 0x00, 0xc6, 0xcb,
+    0xf7, 0xf6, 0x57, 0x9b, 0x6e, 0x0c, 0xac, 0xe8,
+    0x3b, 0xd1, 0xde, 0xb5, 0x34, 0xaf, 0x8b, 0x2a,
+    0xb0, 0x2d, 0x01, 0xeb, 0x7c, 0xa0, 0x42, 0x26,
+    0xbb, 0x2b, 0x43, 0x0e, 0x1d, 0xe2, 0x4e, 0xc9,
+    0xc1, 0x0a, 0x67, 0x1d, 0xfc, 0x83, 0x25, 0xce,
+    0xb2, 0x18, 0xd9, 0x0d, 0x70, 0xf5, 0xa3, 0x5a,
+    0x9c, 0x99, 0xdd, 0x47, 0xa1, 0x57, 0xe7, 0x20,
+    0xde, 0xa1, 0x29, 0x8d, 0x96, 0x62, 0xf9, 0x26,
+    0x95, 0x51, 0xa6, 0xe7, 0x09, 0x8b, 0xba, 0x16,
+    0x8b, 0x19, 0x5b, 0xf9, 0x27, 0x0d, 0xc5, 0xd6,
+    0x5f, 0x02, 0x81, 0x81, 0x00, 0xce, 0x26, 0x31,
+    0xb5, 0x43, 0x53, 0x95, 0x39, 0xdd, 0x01, 0x98,
+    0x8b, 0x3d, 0x27, 0xeb, 0x0b, 0x87, 0x1c, 0x95,
+    0xfc, 0x3e, 0x36, 0x51, 0x31, 0xb5, 0xea, 0x59,
+    0x56, 0xc0, 0x97, 0x62, 0xf0, 0x63, 0x2b, 0xb6,
+    0x30, 0x9b, 0xdf, 0x19, 0x10, 0xe9, 0xa0, 0x3d,
+    0xea, 0x54, 0x5a, 0xe6, 0xc6, 0x9e, 0x7e, 0xb5,
+    0xf0, 0xb0, 0x54, 0xef, 0xc3, 0xe1, 0x47, 0xa6,
+    0x95, 0xc7, 0xe4, 0xa3, 0x4a, 0x30, 0x68, 0x24,
+    0x98, 0x7d, 0xc1, 0x34, 0xa9, 0xcb, 0xbc, 0x3c,
+    0x08, 0x9c, 0x7d, 0x0c, 0xa2, 0xb7, 0x60, 0xaa,
+    0x38, 0x08, 0x16, 0xa6, 0x7f, 0xdb, 0xd2, 0xb1,
+    0x67, 0xe7, 0x93, 0x8e, 0xbb, 0x7e, 0xb9, 0xb5,
+    0xd0, 0xd0, 0x9f, 0x7b, 0xcc, 0x46, 0xe6, 0x74,
+    0x78, 0x1a, 0x96, 0xd6, 0xd7, 0x74, 0x34, 0x54,
+    0x3b, 0x54, 0x55, 0x7f, 0x89, 0x81, 0xbc, 0x40,
+    0x55, 0x87, 0x24, 0x95, 0x33, 0x02, 0x81, 0x81,
+    0x00, 0xb0, 0x18, 0x5d, 0x2a, 0x1a, 0x95, 0x9f,
+    0x9a, 0xd5, 0x3f, 0x37, 0x79, 0xe6, 0x3d, 0x83,
+    0xab, 0x46, 0x86, 0x36, 0x3a, 0x5d, 0x0c, 0x23,
+    0x73, 0x91, 0x2b, 0xda, 0x63, 0xce, 0x46, 0x68,
+    0xd1, 0xfe, 0x40, 0x90, 0xf2, 0x3e, 0x43, 0x2b,
+    0x19, 0x4c, 0xb1, 0xb0, 0xd5, 0x8c, 0x02, 0x21,
+    0x07, 0x18, 0x17, 0xda, 0xe9, 0x49, 0xd7, 0x82,
+    0x73, 0x42, 0x78, 0xd1, 0x82, 0x4e, 0x8a, 0xc0,
+    0xe9, 0x33, 0x2f, 0xcd, 0x62, 0xce, 0x23, 0xca,
+    0xfd, 0x8d, 0xd4, 0x3f, 0x59, 0x80, 0x27, 0xb6,
+    0x61, 0x85, 0x9b, 0x2a, 0xe4, 0xef, 0x5c, 0x36,
+    0x22, 0x21, 0xcd, 0x2a, 0x6d, 0x41, 0x77, 0xe2,
+    0xcb, 0x5d, 0x93, 0x0d, 0x00, 0x10, 0x52, 0x8d,
+    0xd5, 0x92, 0x28, 0x16, 0x78, 0xd3, 0x1a, 0x4c,
+    0x8d, 0xbd, 0x9c, 0x1a, 0x0b, 0x9c, 0x91, 0x16,
+    0x4c, 0xff, 0x31, 0x36, 0xbb, 0xcb, 0x64, 0x1a,
+    0xf7, 0x02, 0x81, 0x80, 0x32, 0x65, 0x09, 0xdf,
+    0xca, 0xee, 0xa2, 0xdb, 0x3b, 0x58, 0xc9, 0x86,
+    0xb8, 0x53, 0x8a, 0xd5, 0x0d, 0x99, 0x82, 0x5c,
+    0xe0, 0x84, 0x7c, 0xc2, 0xcf, 0x3a, 0xd3, 0xce,
+    0x2e, 0x54, 0x93, 0xbe, 0x3a, 0x30, 0x14, 0x60,
+    0xbb, 0xaa, 0x05, 0x41, 0xaa, 0x2b, 0x1f, 0x17,
+    0xaa, 0xb9, 0x72, 0x12, 0xf9, 0xe9, 0xf5, 0xe6,
+    0x39, 0xe4, 0xf9, 0x9c, 0x03, 0xf5, 0x75, 0x16,
+    0xc6, 0x7f, 0xf1, 0x1f, 0x10, 0xc8, 0x54, 0xb1,
+    0xe6, 0x84, 0x15, 0xb0, 0xb0, 0x7a, 0x7a, 0x9e,
+    0x8c, 0x4a, 0xd1, 0x8c, 0xf1, 0x91, 0x32, 0xeb,
+    0x71, 0xa6, 0xbf, 0xdb, 0x1f, 0xcc, 0xd8, 0xcb,
+    0x92, 0xc3, 0xf2, 0xaf, 0x89, 0x22, 0x32, 0xfd,
+    0x32, 0x12, 0xda, 0xbb, 0xac, 0x55, 0x68, 0x01,
+    0x78, 0x56, 0x89, 0x7c, 0xb0, 0x0e, 0x9e, 0xcc,
+    0xc6, 0x28, 0x04, 0x7e, 0x83, 0xf5, 0x96, 0x30,
+    0x92, 0x51, 0xf2, 0x1b, 0x02, 0x81, 0x81, 0x00,
+    0x83, 0x6d, 0xd1, 0x98, 0x90, 0x41, 0x8c, 0xa7,
+    0x92, 0x83, 0xac, 0x89, 0x05, 0x0c, 0x79, 0x67,
+    0x90, 0xb6, 0xa1, 0xf3, 0x2f, 0xca, 0xf0, 0x15,
+    0xe0, 0x30, 0x58, 0xe9, 0x4f, 0xcb, 0x4c, 0x56,
+    0x56, 0x56, 0x14, 0x3f, 0x1b, 0x79, 0xb6, 0xef,
+    0x57, 0x4b, 0x28, 0xbd, 0xb0, 0xe6, 0x0c, 0x49,
+    0x4b, 0xbe, 0xe1, 0x57, 0x28, 0x2a, 0x23, 0x5e,
+    0xc4, 0xa2, 0x19, 0x4b, 0x00, 0x67, 0x78, 0xd9,
+    0x26, 0x6e, 0x17, 0x25, 0xce, 0xe4, 0xfd, 0xde,
+    0x86, 0xa8, 0x5a, 0x67, 0x47, 0x6b, 0x15, 0x09,
+    0xe1, 0xec, 0x8e, 0x62, 0x98, 0x91, 0x6f, 0xc0,
+    0x98, 0x0c, 0x70, 0x0e, 0x7d, 0xbe, 0x63, 0xbd,
+    0x12, 0x5a, 0x98, 0x1c, 0xe3, 0x0c, 0xfb, 0xc7,
+    0xfb, 0x1b, 0xbd, 0x02, 0x87, 0xcc, 0x0c, 0xbb,
+    0xc2, 0xd4, 0xb6, 0xc1, 0xa1, 0x23, 0xd3, 0x1e,
+    0x21, 0x6f, 0x48, 0xba, 0x0e, 0x2e, 0xc7, 0x42
+};
+
+static const uint8_t rsa2048_public_key[] = {
+    0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+    0x00, 0xbd, 0x9c, 0x83, 0x6b, 0x0e, 0x8e, 0xcf,
+    0xfa, 0xaa, 0x4f, 0x6a, 0xf4, 0xe3, 0x52, 0x0f,
+    0xa5, 0xd0, 0xbe, 0x5e, 0x7f, 0x08, 0x24, 0xba,
+    0x87, 0x46, 0xfb, 0x28, 0x93, 0xe5, 0xe5, 0x81,
+    0x42, 0xc0, 0xf9, 0x17, 0xc7, 0x81, 0x01, 0xf4,
+    0x18, 0x6a, 0x17, 0xf5, 0x57, 0x20, 0x37, 0xcf,
+    0xf9, 0x74, 0x5e, 0xe1, 0x48, 0x6a, 0x71, 0x0a,
+    0x0f, 0x79, 0x72, 0x2b, 0x46, 0x10, 0x53, 0xdc,
+    0x14, 0x43, 0xbd, 0xbc, 0x6d, 0x15, 0x6f, 0x15,
+    0x4e, 0xf0, 0x0d, 0x89, 0x39, 0x02, 0xc3, 0x68,
+    0x5c, 0xa8, 0xfc, 0xed, 0x64, 0x9d, 0x98, 0xb7,
+    0xcd, 0x83, 0x66, 0x93, 0xc3, 0xd9, 0x57, 0xa0,
+    0x21, 0x93, 0xad, 0x5c, 0x75, 0x69, 0x88, 0x9e,
+    0x81, 0xdc, 0x7f, 0x1d, 0xd5, 0xbd, 0x1c, 0xc1,
+    0x30, 0x56, 0xa5, 0xda, 0x99, 0x46, 0xa6, 0x6d,
+    0x0e, 0x6f, 0x5e, 0x51, 0x34, 0x49, 0x73, 0xc3,
+    0x67, 0x49, 0x7e, 0x21, 0x2a, 0x20, 0xa7, 0x2b,
+    0x92, 0x73, 0x1d, 0xa5, 0x25, 0x2a, 0xd0, 0x3a,
+    0x89, 0x75, 0xb2, 0xbb, 0x19, 0x37, 0x78, 0x48,
+    0xd2, 0xf2, 0x2a, 0x6d, 0x9e, 0xc6, 0x26, 0xca,
+    0x46, 0x8c, 0xf1, 0x42, 0x2a, 0x31, 0xb2, 0xfc,
+    0xe7, 0x55, 0x51, 0xff, 0x07, 0x13, 0x5b, 0x36,
+    0x59, 0x2b, 0x43, 0x30, 0x4b, 0x05, 0x5c, 0xd2,
+    0x45, 0xa0, 0xa0, 0x7c, 0x17, 0x5b, 0x07, 0xbb,
+    0x5d, 0x83, 0x80, 0x92, 0x6d, 0x87, 0x1a, 0x43,
+    0xac, 0xc7, 0x6b, 0x8d, 0x11, 0x60, 0x27, 0xd2,
+    0xdf, 0xdb, 0x71, 0x02, 0x55, 0x6e, 0xb5, 0xca,
+    0x4d, 0xda, 0x59, 0x0d, 0xb8, 0x8c, 0xcd, 0xd3,
+    0x0e, 0x55, 0xa0, 0xa4, 0x8d, 0xa0, 0x14, 0x10,
+    0x48, 0x42, 0x35, 0x56, 0x08, 0xf7, 0x29, 0x5f,
+    0xa2, 0xea, 0xa4, 0x5e, 0x8e, 0x99, 0x56, 0xaa,
+    0x5a, 0x8c, 0x23, 0x8f, 0x35, 0x22, 0x8a, 0xff,
+    0xed, 0x02, 0x03, 0x01, 0x00, 0x01
+};
+
+static const uint8_t test_sha1_dgst[] = {
+    0x3c, 0x05, 0x19, 0x34, 0x29, 0x19, 0xc7, 0xe0,
+    0x87, 0xb6, 0x24, 0xf9, 0x58, 0xac, 0xa4, 0xd4,
+    0xb2, 0xd9, 0x03, 0x9e,
+};
+
+static const uint8_t exp_signature_rsa2048_pkcs1[] = {
+    0x4e, 0x82, 0x56, 0x4c, 0x84, 0x66, 0xca, 0x1e,
+    0xc6, 0x92, 0x46, 0x20, 0x02, 0x6b, 0x64, 0x46,
+    0x15, 0x6b, 0x24, 0xf2, 0xbb, 0xfa, 0x44, 0x3c,
+    0xaf, 0x42, 0xc8, 0x41, 0xfd, 0xce, 0xed, 0x95,
+    0x34, 0xaf, 0x25, 0x09, 0xd1, 0x06, 0x94, 0xaa,
+    0x52, 0xd4, 0x29, 0xc8, 0x52, 0x34, 0x67, 0x59,
+    0x4f, 0x5a, 0xfd, 0x23, 0x30, 0x5e, 0xc7, 0x1e,
+    0xa6, 0xe0, 0x1b, 0x23, 0xca, 0x82, 0x47, 0x9a,
+    0x2e, 0x2c, 0x66, 0x45, 0x5a, 0x12, 0xa9, 0x15,
+    0xbf, 0xd6, 0xd6, 0xfa, 0x8d, 0x60, 0x99, 0x89,
+    0x91, 0x39, 0x06, 0xb7, 0xd3, 0x9a, 0xef, 0x15,
+    0x7b, 0x95, 0x87, 0x77, 0x2c, 0x41, 0xd4, 0x71,
+    0xd5, 0xdf, 0x22, 0x7b, 0x01, 0xe2, 0xc1, 0xfb,
+    0xb9, 0x4e, 0x0c, 0x9b, 0xd5, 0x04, 0xed, 0x2b,
+    0x7e, 0x73, 0x53, 0xaa, 0x33, 0x89, 0x9d, 0x95,
+    0x28, 0x8f, 0x8b, 0x80, 0x34, 0x7a, 0xea, 0xe3,
+    0x66, 0x8a, 0xa8, 0xad, 0xed, 0x91, 0x43, 0xdd,
+    0x77, 0xe5, 0xd7, 0x16, 0xda, 0xa8, 0x00, 0x29,
+    0x3f, 0x9f, 0xe0, 0x1d, 0x42, 0x9d, 0x35, 0x5d,
+    0x0f, 0xf3, 0x90, 0x27, 0x3a, 0x8c, 0x46, 0x13,
+    0x53, 0x3e, 0x3b, 0x38, 0x77, 0xf8, 0x57, 0x61,
+    0xbc, 0xc4, 0x54, 0x68, 0x48, 0xae, 0x58, 0x03,
+    0x33, 0x94, 0x3f, 0x18, 0x1e, 0xb3, 0x3f, 0x79,
+    0xa7, 0x26, 0x92, 0x5d, 0x32, 0x2a, 0xdb, 0xe6,
+    0x3a, 0xe8, 0xd7, 0xaa, 0x91, 0xfe, 0x9f, 0x06,
+    0x26, 0x68, 0x8c, 0x27, 0x31, 0xb0, 0x04, 0x9e,
+    0x94, 0x79, 0x63, 0xa1, 0xc7, 0xe8, 0x5b, 0x8c,
+    0xd3, 0xf1, 0x88, 0x58, 0x31, 0x2f, 0x4e, 0x11,
+    0x00, 0xfe, 0x29, 0xad, 0x2c, 0xa9, 0x8e, 0x63,
+    0xd8, 0x7d, 0xc5, 0xa1, 0x71, 0xfa, 0x08, 0x29,
+    0xea, 0xd6, 0x6c, 0x53, 0x00, 0x52, 0xa0, 0xed,
+    0x6b, 0x7c, 0x67, 0x50, 0x71, 0x2d, 0x96, 0x7a,
+};
+
+static const uint8_t exp_signature_rsa1024_pkcs1[] = {
+    0x6b, 0x5b, 0xbb, 0x3b, 0x1f, 0x08, 0xd8, 0xc0,
+    0x4a, 0xf1, 0x5a, 0x12, 0xc2, 0x39, 0x14, 0x65,
+    0x4f, 0xda, 0x79, 0x67, 0xf2, 0x89, 0x25, 0xad,
+    0x9e, 0x7e, 0xba, 0xa8, 0x34, 0x15, 0x03, 0xdd,
+    0x80, 0x6b, 0x01, 0xd7, 0x4a, 0xf3, 0xd6, 0xef,
+    0x1e, 0x48, 0xf3, 0xbc, 0x75, 0x1a, 0xc4, 0x2c,
+    0x90, 0x15, 0x9f, 0x21, 0x24, 0x98, 0x21, 0xef,
+    0x6d, 0x3b, 0xf3, 0x82, 0x8f, 0x8d, 0xd8, 0x48,
+    0x37, 0x16, 0x19, 0x8e, 0x3c, 0x64, 0xa0, 0x9e,
+    0xf7, 0x0c, 0xd9, 0x5c, 0xc6, 0x13, 0xc4, 0x5f,
+    0xf8, 0xf3, 0x59, 0x5b, 0xd0, 0x33, 0x95, 0x98,
+    0xde, 0x67, 0x25, 0x58, 0x46, 0xba, 0xee, 0x0f,
+    0x47, 0x7a, 0x7f, 0xd0, 0xe4, 0x77, 0x09, 0x17,
+    0xe9, 0x81, 0x6e, 0x2d, 0x33, 0x9b, 0x13, 0x0b,
+    0xc9, 0xb2, 0x0c, 0x2c, 0xb5, 0xdf, 0x52, 0x8f,
+    0xab, 0x0d, 0xc6, 0x59, 0x1d, 0xc7, 0x33, 0x7b,
+};
+
+static const uint8_t test_plaintext[] = {
+    0x00, 0x44, 0xbc, 0x6f, 0x77, 0xfb, 0xe2, 0xa4,
+    0x98, 0x9e, 0xf5, 0x33, 0xa0, 0xbd, 0x81, 0xb9,
+    0xf1, 0x44, 0x7f, 0x79, 0x89, 0x23, 0xe5, 0x46,
+    0x66, 0x9f, 0x98, 0x95, 0x6f, 0x56, 0x78, 0xf6,
+    0xf5, 0xac, 0x9c, 0xda, 0xc2, 0x79, 0x59, 0xf0,
+    0x1b, 0x03, 0xfa, 0x46, 0x1c, 0x1f, 0x18, 0x07,
+    0xce, 0xad, 0xed, 0x3d, 0x11, 0xf9, 0x1b, 0x26,
+    0x4a, 0x97, 0x28, 0x71, 0x5f, 0x2c, 0x5e, 0x58,
+    0xf0, 0xd6, 0xbf, 0xa4, 0x12, 0xd0, 0x1d, 0x07,
+    0xcb, 0x73, 0x66, 0xb6, 0xa4, 0x09, 0xaf, 0x5d,
+    0xe9, 0x14, 0x14, 0xaf, 0x69, 0xd6, 0xee, 0x0a,
+    0xfc, 0xca, 0xac, 0x94, 0x47, 0xd5, 0x9d, 0x5b,
+    0x2b, 0xfb, 0xce, 0x9d, 0x04, 0xc1, 0xaf, 0xa5,
+    0xa1, 0x8d, 0xa9, 0x48, 0xa8, 0x65, 0xe6, 0x9f,
+    0x74, 0x78, 0x16, 0x32, 0x93, 0xb5, 0x21, 0xb9,
+    0x9f, 0x3f, 0xc1, 0xe5, 0xa2, 0x50, 0x8b, 0x12,
+    0xfb, 0x3e, 0xb0, 0x8a, 0x00, 0xc7, 0x20, 0x56,
+    0xb3, 0xb1, 0x29, 0x95, 0x89, 0xd6, 0x50, 0xf5,
+    0x37, 0x38, 0x8e, 0x12, 0xf1, 0xba, 0x82, 0x37,
+    0x34, 0x68, 0x4b, 0xe8, 0xe3, 0x11, 0x1c, 0x46,
+    0xf9, 0x63, 0x3a, 0xd6, 0xf3, 0x3f, 0x55, 0xa6,
+    0xbd, 0x89, 0xf1, 0x2d, 0x38, 0x91, 0x7c, 0xc2,
+    0x4d, 0xf1, 0x69, 0x82, 0x6d, 0x71, 0x77, 0xf4,
+    0xfc, 0x43, 0x20, 0x6f, 0x43, 0xb9, 0x43, 0xd1,
+    0x65, 0xbd, 0xca, 0xb1, 0x43, 0x87, 0xf8, 0xc8,
+    0x76, 0x21, 0xa9, 0xeb, 0x3e, 0x9a, 0xef, 0xc9,
+    0x0e, 0x79, 0xbc, 0xf0, 0xf8, 0xc8, 0xe2, 0xbc,
+    0x33, 0x35, 0x3e, 0xfc, 0xf9, 0x44, 0x69, 0x06,
+    0x7c, 0x7f, 0x5d, 0xa2, 0x9e, 0xab, 0xc2, 0x82,
+    0xa0, 0xfb, 0xc5, 0x79, 0x57, 0x8c, 0xf1, 0x1c,
+    0x51, 0x64, 0x4c, 0x56, 0x08, 0x80, 0x32, 0xf4,
+    0x97, 0x8f, 0x6f, 0xb2, 0x16, 0xa6, 0x9d, 0x71,
+};
+
+static const uint8_t exp_ciphertext_rsa1024_raw[] = {
+    0x01, 0xa0, 0xc2, 0x94, 0x9f, 0xd6, 0xbe, 0x8d,
+    0xe9, 0x24, 0xaa, 0x9c, 0x67, 0xd7, 0xe3, 0x04,
+    0x34, 0xbf, 0xd3, 0x27, 0xa1, 0x43, 0xeb, 0x60,
+    0x6b, 0x5b, 0x64, 0x15, 0x55, 0x16, 0x98, 0x35,
+    0xc2, 0x59, 0xa7, 0xf7, 0x24, 0xf7, 0x05, 0xb9,
+    0xe8, 0x56, 0x6f, 0xf2, 0x7d, 0x8b, 0x3c, 0xcb,
+    0xa6, 0xc2, 0xac, 0x0c, 0x37, 0x8c, 0x70, 0x70,
+    0x55, 0x05, 0x07, 0x0d, 0x63, 0x6b, 0x7d, 0x5f,
+    0xae, 0x03, 0x1e, 0x55, 0x05, 0xbb, 0xa8, 0xe7,
+    0xff, 0xa0, 0x8c, 0x5b, 0x6b, 0x01, 0x48, 0x2e,
+    0x4f, 0x7f, 0xe2, 0x74, 0xc6, 0x32, 0xa7, 0x2d,
+    0xdb, 0x91, 0x9b, 0x67, 0x4d, 0x71, 0xf9, 0x8c,
+    0x42, 0x43, 0x75, 0x4e, 0xd0, 0x0e, 0x7c, 0xa0,
+    0x97, 0x1a, 0x5f, 0x8e, 0x6f, 0xe4, 0xfa, 0x16,
+    0x1d, 0x59, 0x0e, 0x0b, 0x11, 0x12, 0xa3, 0x0c,
+    0xa6, 0x55, 0xe6, 0xdb, 0xa7, 0x71, 0xa6, 0xff,
+};
+
+static const uint8_t exp_ciphertext_rsa1024_pkcs1[] = {
+    0x93, 0x78, 0x6a, 0x76, 0xb8, 0x94, 0xea, 0xe4,
+    0x32, 0x79, 0x01, 0x8b, 0xc1, 0xcb, 0x2e, 0x2d,
+    0xfe, 0xdc, 0x9b, 0xe3, 0xe9, 0x23, 0xe4, 0x0a,
+    0xb0, 0x6b, 0x9f, 0x6b, 0x62, 0xf5, 0x3d, 0xf0,
+    0x78, 0x84, 0x77, 0x21, 0xad, 0x0b, 0x30, 0x30,
+    0x94, 0xe2, 0x18, 0xc4, 0x9b, 0x12, 0x06, 0xc8,
+    0xaa, 0xf7, 0x30, 0xe4, 0xc8, 0x64, 0xe7, 0x51,
+    0xf1, 0x6a, 0xe1, 0xa2, 0x58, 0x7a, 0x02, 0x9c,
+    0x8e, 0xf0, 0x2d, 0x25, 0x6b, 0xb7, 0x25, 0x5e,
+    0x05, 0xaf, 0x38, 0xb2, 0x69, 0x5e, 0x6c, 0x75,
+    0x6e, 0x27, 0xba, 0x5d, 0x7d, 0x35, 0x72, 0xb7,
+    0x25, 0xd4, 0xaa, 0xb2, 0x4b, 0x9e, 0x6b, 0x82,
+    0xb2, 0x32, 0xe2, 0x13, 0x1d, 0x00, 0x21, 0x08,
+    0xae, 0x14, 0xbb, 0xc0, 0x40, 0xb7, 0x0d, 0xd5,
+    0x0e, 0x4d, 0x6d, 0x9a, 0x70, 0x86, 0xe9, 0xfc,
+    0x67, 0x2b, 0xa4, 0x11, 0x45, 0xb6, 0xc4, 0x2f,
+};
+
+static const uint8_t exp_ciphertext_rsa2048_raw[] = {
+    0x09, 0x7b, 0x9e, 0x7c, 0x10, 0x1f, 0x73, 0xb4,
+    0x5f, 0xdb, 0x4f, 0x05, 0xe7, 0xfc, 0x9e, 0x35,
+    0x48, 0xd8, 0xc8, 0xf5, 0xac, 0x6d, 0xb4, 0xb0,
+    0xd4, 0xf7, 0x69, 0x0f, 0x30, 0x78, 0xbb, 0x55,
+    0x67, 0x66, 0x66, 0x05, 0xf4, 0x77, 0xe2, 0x30,
+    0xa5, 0x94, 0x10, 0xa3, 0xcb, 0xee, 0x13, 0x9f,
+    0x47, 0x1b, 0x2e, 0xf9, 0xfd, 0x94, 0x09, 0xbd,
+    0x26, 0x6e, 0x84, 0xc7, 0x5c, 0x42, 0x20, 0x76,
+    0x72, 0x83, 0x75, 0x68, 0xa4, 0x18, 0x2d, 0x76,
+    0x62, 0xc3, 0xab, 0xc0, 0xc9, 0x36, 0x59, 0xe0,
+    0xa9, 0x70, 0x1f, 0xff, 0x97, 0x07, 0x0d, 0x88,
+    0xc2, 0xd8, 0x51, 0x35, 0xf7, 0xb0, 0x50, 0xe4,
+    0x9f, 0x3d, 0xd4, 0x71, 0x8b, 0x40, 0x89, 0x71,
+    0x6c, 0xd8, 0xc2, 0x63, 0xb6, 0x3a, 0xce, 0xb1,
+    0x32, 0xf1, 0xc6, 0x11, 0x31, 0x25, 0x48, 0xcf,
+    0xeb, 0xbc, 0xd3, 0x9b, 0xc5, 0xbd, 0xd2, 0x57,
+    0x73, 0x9b, 0x20, 0xb8, 0xdf, 0xbe, 0xb8, 0x40,
+    0xb6, 0xac, 0x24, 0xdb, 0x94, 0x6a, 0x93, 0x43,
+    0x4a, 0xa8, 0xa3, 0xcf, 0xd5, 0x61, 0x1b, 0x46,
+    0x1d, 0x6f, 0x57, 0xec, 0xa6, 0xd0, 0x44, 0x05,
+    0x48, 0xb8, 0x90, 0x80, 0x23, 0x8e, 0x5f, 0xb0,
+    0x4b, 0x6f, 0xe3, 0xf9, 0xb0, 0x04, 0x60, 0xae,
+    0x80, 0xcf, 0xa5, 0x5c, 0x11, 0xe4, 0xce, 0x57,
+    0x5b, 0xbb, 0xde, 0x92, 0xfc, 0xe7, 0x3f, 0xe0,
+    0xfc, 0x06, 0xc8, 0xf3, 0x8c, 0xac, 0x86, 0x09,
+    0x31, 0xe5, 0x7e, 0xfb, 0x5d, 0xa7, 0x57, 0xf8,
+    0x1d, 0x23, 0x9d, 0xa3, 0xeb, 0x53, 0x28, 0xde,
+    0xbf, 0x53, 0xef, 0x35, 0x3c, 0x7e, 0x3c, 0x1b,
+    0x76, 0x9d, 0x09, 0x25, 0x43, 0xd4, 0x8b, 0xca,
+    0xda, 0x45, 0x5b, 0xdc, 0x9f, 0x57, 0x5a, 0x30,
+    0x2e, 0xe9, 0x73, 0x68, 0x28, 0xfa, 0x40, 0xb0,
+    0x7c, 0x31, 0xd7, 0x8b, 0x4e, 0x99, 0x94, 0xf1,
+};
+
+static const uint8_t exp_ciphertext_rsa2048_pkcs1[] = {
+    0xa5, 0x19, 0x19, 0x34, 0xad, 0xf6, 0xd2, 0xbe,
+    0xed, 0x8f, 0xe5, 0xfe, 0xa2, 0xa5, 0x20, 0x08,
+    0x15, 0x53, 0x7c, 0x68, 0x28, 0xae, 0x07, 0xb2,
+    0x4c, 0x5d, 0xee, 0xc1, 0xc6, 0xdc, 0xd6, 0x8b,
+    0xc6, 0xba, 0x46, 0xe1, 0x16, 0xa9, 0x04, 0x72,
+    0xdf, 0x8f, 0x1e, 0x97, 0x2a, 0x55, 0xe7, 0xac,
+    0x08, 0x0d, 0x61, 0xe8, 0x64, 0x8b, 0x6f, 0x96,
+    0x0e, 0xbb, 0x8a, 0x30, 0xb3, 0x73, 0x28, 0x61,
+    0x16, 0x89, 0x90, 0x88, 0x8e, 0xda, 0x22, 0xe6,
+    0x42, 0x16, 0xc7, 0xe8, 0x30, 0x0d, 0x7f, 0x44,
+    0x1e, 0xef, 0xe6, 0xdb, 0x78, 0x54, 0x89, 0xa5,
+    0x60, 0x67, 0xb3, 0x35, 0x2d, 0x79, 0x49, 0xcf,
+    0xe6, 0x8f, 0xf3, 0x64, 0x52, 0x1c, 0x6c, 0x43,
+    0x7e, 0xb0, 0xde, 0x55, 0xdf, 0xbe, 0xb7, 0xb1,
+    0xdb, 0x02, 0xee, 0x76, 0x96, 0xcc, 0x0b, 0x97,
+    0x8c, 0x23, 0xaa, 0x7d, 0x4c, 0x47, 0x28, 0x41,
+    0x7a, 0x20, 0x39, 0x1f, 0x64, 0x0b, 0xf1, 0x74,
+    0xf1, 0x29, 0xda, 0xe9, 0x3a, 0x36, 0xa6, 0x88,
+    0xb8, 0xc0, 0x21, 0xb8, 0x9b, 0x5d, 0x90, 0x85,
+    0xa3, 0x30, 0x61, 0x17, 0x8c, 0x74, 0x63, 0xd5,
+    0x0f, 0x95, 0xdc, 0xc8, 0x4f, 0xa7, 0x24, 0x55,
+    0x40, 0xe2, 0x84, 0x57, 0x65, 0x06, 0x11, 0x30,
+    0x2b, 0x9e, 0x32, 0x95, 0x39, 0xf2, 0x1a, 0x3f,
+    0xab, 0xcd, 0x7b, 0x7f, 0x9c, 0xf0, 0x00, 0x50,
+    0x7c, 0xf4, 0xbe, 0xcb, 0x80, 0xea, 0x66, 0xba,
+    0x0e, 0x7b, 0x46, 0x0b, 0x25, 0xe0, 0xc1, 0x03,
+    0x29, 0x11, 0x2d, 0x69, 0x4f, 0x21, 0xa2, 0x58,
+    0x37, 0x4b, 0x84, 0x15, 0xb3, 0x65, 0x3a, 0xac,
+    0xd4, 0xd0, 0xf6, 0xdf, 0x4b, 0x82, 0xca, 0x9e,
+    0xbb, 0xbe, 0x3c, 0x4d, 0xd5, 0xbf, 0x00, 0xd6,
+    0x12, 0x48, 0x72, 0x0b, 0xc7, 0xf8, 0xe1, 0xcd,
+    0xd0, 0x28, 0x03, 0x19, 0xa6, 0x06, 0x13, 0x45,
+};
+
+typedef struct QCryptoAkCipherTestData QCryptoAkCipherTestData;
+struct QCryptoAkCipherTestData {
+    const char *path;
+    QCryptoAkCipherOptions opt;
+
+    const uint8_t *priv_key;
+    size_t priv_key_len;
+    const uint8_t *pub_key;
+    size_t pub_key_len;
+
+    const uint8_t *plaintext;
+    size_t plen;
+    const uint8_t *ciphertext;
+    size_t clen;
+    const uint8_t *dgst;
+    size_t dlen;
+    const uint8_t *signature;
+    size_t slen;
+};
+
+static QCryptoAkCipherTestData test_data[] = {
+    /* rsa1024 with raw padding */
+    {
+        .path = "/crypto/akcipher/rsa1024-raw",
+        .opt = {
+            .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+            .u.rsa = {
+                .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW,
+            },
+        },
+        .pub_key = rsa1024_public_key,
+        .pub_key_len = sizeof(rsa1024_public_key),
+        .priv_key = rsa1024_private_key,
+        .priv_key_len = sizeof(rsa1024_private_key),
+
+        .plaintext = test_plaintext,
+        .plen = 128,
+        .ciphertext = exp_ciphertext_rsa1024_raw,
+        .clen = sizeof(exp_ciphertext_rsa1024_raw),
+    },
+
+    /* rsa1024 with pkcs1 padding */
+    {
+        .path = "/crypto/akcipher/rsa1024-pkcs1",
+        .opt = {
+            .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+            .u.rsa = {
+                .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1,
+                .hash_alg = QCRYPTO_HASH_ALG_SHA1,
+            },
+        },
+        .pub_key = rsa1024_public_key,
+        .pub_key_len = sizeof(rsa1024_public_key),
+        .priv_key = rsa1024_private_key,
+        .priv_key_len = sizeof(rsa1024_private_key),
+
+        .plaintext = test_plaintext,
+        .plen = 64,
+        .ciphertext = exp_ciphertext_rsa1024_pkcs1,
+        .clen = sizeof(exp_ciphertext_rsa1024_pkcs1),
+        .dgst = test_sha1_dgst,
+        .dlen = sizeof(test_sha1_dgst),
+        .signature = exp_signature_rsa1024_pkcs1,
+        .slen = sizeof(exp_signature_rsa1024_pkcs1),
+    },
+
+    /* rsa2048 with raw padding */
+    {
+        .path = "/crypto/akcipher/rsa2048-raw",
+        .opt = {
+            .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+            .u.rsa = {
+                .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW,
+            },
+        },
+        .pub_key = rsa2048_public_key,
+        .pub_key_len = sizeof(rsa2048_public_key),
+        .priv_key = rsa2048_private_key,
+        .priv_key_len = sizeof(rsa2048_private_key),
+
+        .plaintext = test_plaintext,
+        .plen = 256,
+        .ciphertext = exp_ciphertext_rsa2048_raw,
+        .clen = sizeof(exp_ciphertext_rsa2048_raw),
+    },
+
+    /* rsa2048 with pkcs1 padding */
+    {
+        .path = "/crypto/akcipher/rsa2048-pkcs1",
+        .opt = {
+            .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+            .u.rsa = {
+                .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1,
+                .hash_alg = QCRYPTO_HASH_ALG_SHA1,
+            },
+        },
+        .pub_key = rsa2048_public_key,
+        .pub_key_len = sizeof(rsa2048_public_key),
+        .priv_key = rsa2048_private_key,
+        .priv_key_len = sizeof(rsa2048_private_key),
+
+        .plaintext = test_plaintext,
+        .plen = 128,
+        .ciphertext = exp_ciphertext_rsa2048_pkcs1,
+        .clen = sizeof(exp_ciphertext_rsa2048_pkcs1),
+        .dgst = test_sha1_dgst,
+        .dlen = sizeof(test_sha1_dgst),
+        .signature = exp_signature_rsa2048_pkcs1,
+        .slen = sizeof(exp_signature_rsa2048_pkcs1),
+    },
+
+};
+
+static void test_akcipher(const void *opaque)
+{
+    const QCryptoAkCipherTestData *data = opaque;
+    g_autofree uint8_t *plaintext = NULL;
+    g_autofree uint8_t *ciphertext = NULL;
+    g_autofree uint8_t *signature = NULL;
+    QCryptoAkCipher *pub_key, *priv_key;
+
+    if (!qcrypto_akcipher_supports((QCryptoAkCipherOptions *)&data->opt)) {
+        return;
+    }
+    pub_key = qcrypto_akcipher_new(&data->opt,
+                                   QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+                                   data->pub_key, data->pub_key_len,
+                                   &error_abort);
+    g_assert(pub_key != NULL);
+    priv_key = qcrypto_akcipher_new(&data->opt,
+                                    QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+                                    data->priv_key, data->priv_key_len,
+                                    &error_abort);
+    g_assert(priv_key != NULL);
+
+    if (data->plaintext != NULL) {
+
+        ciphertext = g_new0(uint8_t, data->clen);
+        g_assert(qcrypto_akcipher_encrypt(pub_key, data->plaintext, data->plen,
+                                          ciphertext, data->clen,
+                                          &error_abort) > 0);
+
+        /**
+         * In the asymmetric encryption algorithms, the ciphertext generated
+         * each time may be different, here only compare the decrypted
+         * plaintext
+         */
+        plaintext = g_new0(uint8_t, data->clen);
+        g_assert(qcrypto_akcipher_decrypt(priv_key, ciphertext,
+                                          data->clen, plaintext,
+                                          data->plen,
+                                          &error_abort) == data->plen);
+        g_assert(!memcmp(plaintext, data->plaintext, data->plen));
+    }
+
+    if (data->signature != NULL) {
+        signature = g_new(uint8_t, data->slen);
+        g_assert(qcrypto_akcipher_sign(priv_key, data->dgst, data->dlen,
+                                       signature, data->slen,
+                                       &error_abort) > 0);
+        /**
+         * The signature generated each time may be different, here only check
+         * the verification.
+         */
+        g_assert(qcrypto_akcipher_verify(pub_key, data->signature, data->slen,
+                                         data->dgst, data->dlen,
+                                         &error_abort) == 0);
+        g_assert(qcrypto_akcipher_verify(pub_key, signature, data->slen,
+                                         data->dgst, data->dlen,
+                                         &error_abort) == 0);
+        ++signature[0];
+        /* Here error should be ignored */
+        g_assert(qcrypto_akcipher_verify(pub_key, signature, data->slen,
+                                         data->dgst, data->dlen, NULL) != 0);
+    }
+
+    qcrypto_akcipher_free(pub_key);
+    qcrypto_akcipher_free(priv_key);
+}
+
+int main(int argc, char **argv)
+{
+    size_t i;
+    g_test_init(&argc, &argv, NULL);
+    g_assert(qcrypto_init(NULL) == 0);
+
+    for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
+        g_test_add_data_func(test_data[i].path, &test_data[i], test_akcipher);
+    }
+
+    return g_test_run();
+}
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (6 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-13 10:35   ` Daniel P. Berrangé
  2022-04-28 13:59 ` [PATCH v5 9/9] crypto: Introduce RSA algorithm zhenwei pi
  2022-05-13 10:19 ` [PATCH v5 0/9] Introduce akcipher service for virtio-crypto Michael S. Tsirkin
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang, cohuck

From: Lei He <helei.sig11@bytedance.com>

As Daniel suggested, Add tests suite for rsakey, as a way to prove
that we can handle DER errors correctly.

Signed-off-by: lei he <helei.sig11@bytedance.com>
---
 tests/unit/test-crypto-akcipher.c | 285 +++++++++++++++++++++++++++++-
 1 file changed, 282 insertions(+), 3 deletions(-)

diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c
index b5be563884..4f1f4214dd 100644
--- a/tests/unit/test-crypto-akcipher.c
+++ b/tests/unit/test-crypto-akcipher.c
@@ -517,6 +517,158 @@ static const uint8_t exp_ciphertext_rsa2048_pkcs1[] = {
     0xd0, 0x28, 0x03, 0x19, 0xa6, 0x06, 0x13, 0x45,
 };
 
+static const uint8_t rsa_private_key_lack_element[] = {
+    /* RSAPrivateKey, offset: 0, length: 176 */
+    0x30, 0x81, 0xb0,
+    /* version, offset: 4, length: 1 */
+    0x02, 0x01, 0x00,
+    /* n, offset: 7, length: 65 */
+    0x02, 0x41,
+    0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+    0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+    0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+    0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+    0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+    0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+    0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+    0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+    0xf7,
+    /* e, offset: 74, length: 3 */
+    0x02, 0x03, 0x01, 0x00, 0x01,
+    /* d, offset: 79, length: 64 */
+    0x02, 0x40,
+    0x1e, 0x80, 0xfe, 0xda, 0x65, 0xdb, 0x70, 0xb8,
+    0x61, 0x91, 0x28, 0xbf, 0x6c, 0x32, 0xc1, 0x05,
+    0xd1, 0x26, 0x6a, 0x1c, 0x83, 0xcc, 0xf4, 0x1f,
+    0x53, 0x42, 0x72, 0x1f, 0x62, 0x57, 0x0a, 0xc4,
+    0x66, 0x76, 0x30, 0x87, 0xb9, 0xb1, 0xb9, 0x6a,
+    0x63, 0xfd, 0x8f, 0x3e, 0xfc, 0x35, 0x3f, 0xd6,
+    0x2e, 0x6c, 0xc8, 0x70, 0x8a, 0x17, 0xc1, 0x28,
+    0x6a, 0xfe, 0x51, 0x56, 0xb3, 0x92, 0x6f, 0x09,
+    /* p, offset: 145, length: 33 */
+    0x02, 0x21,
+    0x00, 0xe3, 0x2e, 0x2d, 0x8d, 0xba, 0x1c, 0x34,
+    0x4c, 0x49, 0x9f, 0xc1, 0xa6, 0xdd, 0xd7, 0x13,
+    0x8d, 0x05, 0x48, 0xdd, 0xff, 0x5c, 0x30, 0xbc,
+    0x6b, 0xc4, 0x18, 0x9d, 0xfc, 0xa2, 0xd0, 0x9b,
+    0x4d,
+    /* q, offset: 180, length: 33 */
+    0x02, 0x21,
+    0x00, 0xd1, 0x75, 0xaf, 0x4b, 0xc6, 0x1a, 0xb0,
+    0x98, 0x14, 0x42, 0xae, 0x33, 0xf3, 0x44, 0xde,
+    0x21, 0xcb, 0x04, 0xda, 0xfb, 0x1e, 0x35, 0x92,
+    0xcd, 0x69, 0xc0, 0x83, 0x06, 0x83, 0x8e, 0x39,
+    0x53,
+    /* lack element: dp, dq, u */
+};
+
+static const uint8_t rsa_public_key_lack_element[] = {
+    /* RSAPublicKey, offset: 0, length: 67 */
+    0x30, 0x81, 0x43,
+    /* n, offset: 7, length: 65 */
+    0x02, 0x41,
+    0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+    0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+    0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+    0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+    0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+    0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+    0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+    0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+    0xf7,
+    /* lack element: e */
+};
+
+static const uint8_t rsa_public_key_empty_element[] = {
+    /* RSAPublicKey, offset: 0, length: 69 */
+    0x30, 0x81, 0x45,
+    /* n, offset: 7, length: 65 */
+    0x02, 0x41,
+    0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+    0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+    0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+    0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+    0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+    0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+    0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+    0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+    0xf7,
+    /* e: empty element */
+    0x02, 0x00,
+};
+
+static const uint8_t rsa_private_key_empty_element[] = {
+    /* RSAPrivateKey, offset: 0, length: 19 */
+    0x30, 0x81, 0x13,
+    /* version, offset: 4, length: 1 */
+    0x02, 0x01, 0x00,
+    /* n: empty element */
+    0x02, 0x00,
+    /* e: empty element */
+    0x02, 0x00,
+    /* d: empty element */
+    0x02, 0x00,
+    /* p: empty element */
+    0x02, 0x00,
+    /* q: empty element */
+    0x02, 0x00,
+    /* dp: empty element */
+    0x02, 0x00,
+    /* dq: empty element */
+    0x02, 0x00,
+    /* u: empty element */
+    0x02, 0x00,
+};
+
+static const uint8_t rsa_public_key_invalid_length_val[] = {
+    /* RSAPublicKey, INVALID length: 313 */
+    0x30, 0x82, 0x01, 0x39,
+    /* n, offset: 7, length: 65 */
+    0x02, 0x41,
+    0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+    0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+    0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+    0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+    0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+    0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+    0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+    0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+    0xf7,
+    /* e, */
+    0x02, 0x03, 0x01, 0x00, 0x01,  /* INTEGER, offset: 74, length: 3 */
+};
+
+static const uint8_t rsa_public_key_extra_elem[] = {
+    /* RSAPublicKey, length: 80 */
+    0x30, 0x81, 0x50,
+    /* n, offset: 7, length: 65 */
+    0x02, 0x41,
+    0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+    0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+    0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+    0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+    0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+    0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+    0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+    0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+    0xf7,
+    /* e, offset: 74, length: 3 */
+    0x02, 0x03, 0x01, 0x00, 0x01,
+    /* Additional integer field, length 3 */
+    0x02, 0x06, 0xe1, 0x22, 0xdb, 0xe1, 0x22, 0xdb,
+};
+
+typedef struct QCryptoRSAKeyTestData QCryptoRSAKeyTestData;
+struct QCryptoRSAKeyTestData {
+    const char *path;
+    QCryptoAkCipherKeyType key_type;
+    QCryptoAkCipherOptions opt;
+    const uint8_t *key;
+    size_t keylen;
+    bool is_valid_key;
+    size_t exp_key_len;
+};
+
 typedef struct QCryptoAkCipherTestData QCryptoAkCipherTestData;
 struct QCryptoAkCipherTestData {
     const char *path;
@@ -537,7 +689,98 @@ struct QCryptoAkCipherTestData {
     size_t slen;
 };
 
-static QCryptoAkCipherTestData test_data[] = {
+static QCryptoRSAKeyTestData rsakey_test_data[] = {
+    {
+        .path = "/crypto/akcipher/rsakey-1024-public",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa1024_public_key,
+        .keylen = sizeof(rsa1024_public_key),
+        .is_valid_key = true,
+        .exp_key_len = 128,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-1024-private",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+        .key = rsa1024_private_key,
+        .keylen = sizeof(rsa1024_private_key),
+        .is_valid_key = true,
+        .exp_key_len = 128,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-2048-public",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa2048_public_key,
+        .keylen = sizeof(rsa2048_public_key),
+        .is_valid_key = true,
+        .exp_key_len = 256,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-2048-private",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+        .key = rsa2048_private_key,
+        .keylen = sizeof(rsa2048_private_key),
+        .is_valid_key = true,
+        .exp_key_len = 256,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-public-lack-elem",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa_public_key_lack_element,
+        .keylen = sizeof(rsa_public_key_lack_element),
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-private-lack-elem",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+        .key = rsa_private_key_lack_element,
+        .keylen = sizeof(rsa_private_key_lack_element),
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-public-empty-elem",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa_public_key_empty_element,
+        .keylen = sizeof(rsa_public_key_empty_element),
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-private-empty-elem",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+        .key = rsa_private_key_empty_element,
+        .keylen = sizeof(rsa_private_key_empty_element),
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-public-empty-key",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = NULL,
+        .keylen = 0,
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-private-empty-key",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+        .key = NULL,
+        .keylen = 0,
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-public-invalid-length-val",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa_public_key_invalid_length_val,
+        .keylen = sizeof(rsa_public_key_invalid_length_val),
+        .is_valid_key = false,
+    },
+    {
+        .path = "/crypto/akcipher/rsakey-public-extra-elem",
+        .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+        .key = rsa_public_key_extra_elem,
+        .keylen = sizeof(rsa_public_key_extra_elem),
+        .is_valid_key = false,
+    },
+};
+
+static QCryptoAkCipherTestData akcipher_test_data[] = {
     /* rsa1024 with raw padding */
     {
         .path = "/crypto/akcipher/rsa1024-raw",
@@ -697,14 +940,50 @@ static void test_akcipher(const void *opaque)
     qcrypto_akcipher_free(priv_key);
 }
 
+static void test_rsakey(const void *opaque)
+{
+    const QCryptoRSAKeyTestData *data = (const QCryptoRSAKeyTestData *)opaque;
+    QCryptoAkCipherOptions opt = {
+        .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+        .u.rsa = {
+            .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1,
+            .hash_alg = QCRYPTO_HASH_ALG_SHA1,
+        }
+    };
+    g_autoptr(QCryptoAkCipher) key = qcrypto_akcipher_new(
+        &opt, data->key_type, data->key, data->keylen, NULL);
+
+    if (!qcrypto_akcipher_supports(&opt)) {
+        return;
+    }
+
+    if (!data->is_valid_key) {
+        g_assert(key == NULL);
+        return;
+    }
+
+    g_assert(key != NULL);
+    g_assert(qcrypto_akcipher_max_ciphertext_len(key) == data->exp_key_len);
+    g_assert(qcrypto_akcipher_max_plaintext_len(key) == data->exp_key_len);
+    g_assert(qcrypto_akcipher_max_signature_len(key) == data->exp_key_len);
+    g_assert(qcrypto_akcipher_max_dgst_len(key) == data->exp_key_len);
+}
+
 int main(int argc, char **argv)
 {
     size_t i;
     g_test_init(&argc, &argv, NULL);
     g_assert(qcrypto_init(NULL) == 0);
 
-    for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
-        g_test_add_data_func(test_data[i].path, &test_data[i], test_akcipher);
+    for (i = 0; i < G_N_ELEMENTS(akcipher_test_data); i++) {
+        g_test_add_data_func(akcipher_test_data[i].path,
+                             &akcipher_test_data[i],
+                             test_akcipher);
+    }
+    for (i = 0; i < G_N_ELEMENTS(rsakey_test_data); i++) {
+        g_test_add_data_func(rsakey_test_data[i].path,
+                             &rsakey_test_data[i],
+                             test_rsakey);
     }
 
     return g_test_run();
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 9/9] crypto: Introduce RSA algorithm
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (7 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys zhenwei pi
@ 2022-04-28 13:59 ` zhenwei pi
  2022-05-09  2:17   ` PING: " zhenwei pi
  2022-05-13 10:19 ` [PATCH v5 0/9] Introduce akcipher service for virtio-crypto Michael S. Tsirkin
  9 siblings, 1 reply; 23+ messages in thread
From: zhenwei pi @ 2022-04-28 13:59 UTC (permalink / raw)
  To: mst, arei.gonglei, berrange
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, jasowang,
	cohuck, zhenwei pi

There are two parts in this patch:
1, support akcipher service by cryptodev-builtin driver
2, virtio-crypto driver supports akcipher service

In principle, we should separate this into two patches, to avoid
compiling error, merge them into one.

Then virtio-crypto gets request from guest side, and forwards the
request to builtin driver to handle it.

Test with a guest linux:
1, The self-test framework of crypto layer works fine in guest kernel
2, Test with Linux guest(with asym support), the following script
test(note that pkey_XXX is supported only in a newer version of keyutils):
  - both public key & private key
  - create/close session
  - encrypt/decrypt/sign/verify basic driver operation
  - also test with kernel crypto layer(pkey add/query)

All the cases work fine.

Run script in guest:
rm -rf *.der *.pem *.pfx
modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
rm -rf /tmp/data
dd if=/dev/random of=/tmp/data count=1 bs=20

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj "/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=qemu@qemu.org"
openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der

PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
echo "priv key id = "$PRIV_KEY_ID
PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
echo "pub key id = "$PUB_KEY_ID

keyctl pkey_query $PRIV_KEY_ID 0
keyctl pkey_query $PUB_KEY_ID 0

echo "Enc with priv key..."
keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
echo "Dec with pub key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Sign with priv key..."
keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
echo "Verify with pub key..."
keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

echo "Enc with pub key..."
keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
echo "Dec with priv key..."
keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec

echo "Verify with pub key..."
keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Signed-off-by: lei he <helei.sig11@bytedance.com
---
 backends/cryptodev-builtin.c      | 272 +++++++++++++++++++++----
 backends/cryptodev-vhost-user.c   |  34 +++-
 backends/cryptodev.c              |  32 ++-
 hw/virtio/virtio-crypto.c         | 323 ++++++++++++++++++++++++------
 include/hw/virtio/virtio-crypto.h |   5 +-
 include/sysemu/cryptodev.h        |  83 ++++++--
 6 files changed, 604 insertions(+), 145 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 0671bf9f3e..388aedd8df 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "standard-headers/linux/virtio_crypto.h"
 #include "crypto/cipher.h"
+#include "crypto/akcipher.h"
 #include "qom/object.h"
 
 
@@ -41,11 +42,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN)
 typedef struct CryptoDevBackendBuiltinSession {
     QCryptoCipher *cipher;
     uint8_t direction; /* encryption or decryption */
-    uint8_t type; /* cipher? hash? aead? */
+    uint8_t type; /* cipher? hash? aead? akcipher? */
+    QCryptoAkCipher *akcipher;
     QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
 } CryptoDevBackendBuiltinSession;
 
-/* Max number of symmetric sessions */
+/* Max number of symmetric/asymmetric sessions */
 #define MAX_NUM_SESSIONS 256
 
 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN    512
@@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
     backend->conf.crypto_services =
                          1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
                          1u << VIRTIO_CRYPTO_SERVICE_HASH |
-                         1u << VIRTIO_CRYPTO_SERVICE_MAC;
+                         1u << VIRTIO_CRYPTO_SERVICE_MAC |
+                         1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
     backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
     backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+    backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
     /*
      * Set the Maximum length of crypto request.
      * Why this value? Just avoid to overflow when
      * memory allocation for each crypto request.
      */
-    backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+    backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
     backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
     backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
 
@@ -148,6 +152,53 @@ err:
    return -1;
 }
 
+static int cryptodev_builtin_get_rsa_hash_algo(
+    int virtio_rsa_hash, Error **errp)
+{
+    switch (virtio_rsa_hash) {
+    case VIRTIO_CRYPTO_RSA_MD5:
+        return QCRYPTO_HASH_ALG_MD5;
+
+    case VIRTIO_CRYPTO_RSA_SHA1:
+        return QCRYPTO_HASH_ALG_SHA1;
+
+    case VIRTIO_CRYPTO_RSA_SHA256:
+        return QCRYPTO_HASH_ALG_SHA256;
+
+    case VIRTIO_CRYPTO_RSA_SHA512:
+        return QCRYPTO_HASH_ALG_SHA512;
+
+    default:
+        error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+        return -1;
+    }
+}
+
+static int cryptodev_builtin_set_rsa_options(
+                    int virtio_padding_algo,
+                    int virtio_hash_algo,
+                    QCryptoAkCipherOptionsRSA *opt,
+                    Error **errp)
+{
+    if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+        opt->hash_alg =
+            cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
+        if (opt->hash_alg < 0) {
+            return -1;
+        }
+        return 0;
+    }
+
+    if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+        return 0;
+    }
+
+    error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
+    return -1;
+}
+
 static int cryptodev_builtin_create_cipher_session(
                     CryptoDevBackendBuiltin *builtin,
                     CryptoDevBackendSymSessionInfo *sess_info,
@@ -240,26 +291,89 @@ static int cryptodev_builtin_create_cipher_session(
     return index;
 }
 
-static int64_t cryptodev_builtin_sym_create_session(
+static int cryptodev_builtin_create_akcipher_session(
+                    CryptoDevBackendBuiltin *builtin,
+                    CryptoDevBackendAsymSessionInfo *sess_info,
+                    Error **errp)
+{
+    CryptoDevBackendBuiltinSession *sess;
+    QCryptoAkCipher *akcipher;
+    int index;
+    QCryptoAkCipherKeyType type;
+    QCryptoAkCipherOptions opts;
+
+    switch (sess_info->algo) {
+    case VIRTIO_CRYPTO_AKCIPHER_RSA:
+        opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+        if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo,
+            sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) {
+            return -1;
+        }
+        break;
+
+    /* TODO support DSA&ECDSA until qemu crypto framework support these */
+
+    default:
+        error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo);
+        return -1;
+    }
+
+    switch (sess_info->keytype) {
+    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+        type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+        type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
+        break;
+
+    default:
+        error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype);
+        return -1;
+    }
+
+    index = cryptodev_builtin_get_unused_session_index(builtin);
+    if (index < 0) {
+        error_setg(errp, "Total number of sessions created exceeds %u",
+                   MAX_NUM_SESSIONS);
+        return -1;
+    }
+
+    akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key,
+                                    sess_info->keylen, errp);
+    if (!akcipher) {
+        return -1;
+    }
+
+    sess = g_new0(CryptoDevBackendBuiltinSession, 1);
+    sess->akcipher = akcipher;
+
+    builtin->sessions[index] = sess;
+
+    return index;
+}
+
+static int64_t cryptodev_builtin_create_session(
            CryptoDevBackend *backend,
-           CryptoDevBackendSymSessionInfo *sess_info,
+           CryptoDevBackendSessionInfo *sess_info,
            uint32_t queue_index, Error **errp)
 {
     CryptoDevBackendBuiltin *builtin =
                       CRYPTODEV_BACKEND_BUILTIN(backend);
-    int64_t session_id = -1;
-    int ret;
+    CryptoDevBackendSymSessionInfo *sym_sess_info;
+    CryptoDevBackendAsymSessionInfo *asym_sess_info;
 
     switch (sess_info->op_code) {
     case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
-        ret = cryptodev_builtin_create_cipher_session(
-                           builtin, sess_info, errp);
-        if (ret < 0) {
-            return ret;
-        } else {
-            session_id = ret;
-        }
-        break;
+        sym_sess_info = &sess_info->u.sym_sess_info;
+        return cryptodev_builtin_create_cipher_session(
+                           builtin, sym_sess_info, errp);
+
+    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+        asym_sess_info = &sess_info->u.asym_sess_info;
+        return cryptodev_builtin_create_akcipher_session(
+                           builtin, asym_sess_info, errp);
+
     case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
     case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
     default:
@@ -268,50 +382,44 @@ static int64_t cryptodev_builtin_sym_create_session(
         return -1;
     }
 
-    return session_id;
+    return -1;
 }
 
-static int cryptodev_builtin_sym_close_session(
+static int cryptodev_builtin_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
            uint32_t queue_index, Error **errp)
 {
     CryptoDevBackendBuiltin *builtin =
                       CRYPTODEV_BACKEND_BUILTIN(backend);
+    CryptoDevBackendBuiltinSession *session;
 
     assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
 
-    qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
-    g_free(builtin->sessions[session_id]);
+    session = builtin->sessions[session_id];
+    if (session->cipher) {
+        qcrypto_cipher_free(session->cipher);
+    } else if (session->akcipher) {
+        qcrypto_akcipher_free(session->akcipher);
+    }
+
+    g_free(session);
     builtin->sessions[session_id] = NULL;
     return 0;
 }
 
 static int cryptodev_builtin_sym_operation(
-                 CryptoDevBackend *backend,
-                 CryptoDevBackendSymOpInfo *op_info,
-                 uint32_t queue_index, Error **errp)
+                 CryptoDevBackendBuiltinSession *sess,
+                 CryptoDevBackendSymOpInfo *op_info, Error **errp)
 {
-    CryptoDevBackendBuiltin *builtin =
-                      CRYPTODEV_BACKEND_BUILTIN(backend);
-    CryptoDevBackendBuiltinSession *sess;
     int ret;
 
-    if (op_info->session_id >= MAX_NUM_SESSIONS ||
-              builtin->sessions[op_info->session_id] == NULL) {
-        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
-                   op_info->session_id);
-        return -VIRTIO_CRYPTO_INVSESS;
-    }
-
     if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
         error_setg(errp,
                "Algorithm chain is unsupported for cryptdoev-builtin");
         return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    sess = builtin->sessions[op_info->session_id];
-
     if (op_info->iv_len > 0) {
         ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
                                    op_info->iv_len, errp);
@@ -333,9 +441,95 @@ static int cryptodev_builtin_sym_operation(
             return -VIRTIO_CRYPTO_ERR;
         }
     }
+
+    return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_builtin_asym_operation(
+                 CryptoDevBackendBuiltinSession *sess, uint32_t op_code,
+                 CryptoDevBackendAsymOpInfo *op_info, Error **errp)
+{
+    int ret;
+
+    switch (op_code) {
+    case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+        ret = qcrypto_akcipher_encrypt(sess->akcipher,
+                                       op_info->src, op_info->src_len,
+                                       op_info->dst, op_info->dst_len, errp);
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+        ret = qcrypto_akcipher_decrypt(sess->akcipher,
+                                       op_info->src, op_info->src_len,
+                                       op_info->dst, op_info->dst_len, errp);
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+        ret = qcrypto_akcipher_sign(sess->akcipher,
+                                    op_info->src, op_info->src_len,
+                                    op_info->dst, op_info->dst_len, errp);
+        break;
+
+    case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+        ret = qcrypto_akcipher_verify(sess->akcipher,
+                                      op_info->src, op_info->src_len,
+                                      op_info->dst, op_info->dst_len, errp);
+    break;
+
+    default:
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    if (ret < 0) {
+        if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+            return -VIRTIO_CRYPTO_KEY_REJECTED;
+        }
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    /* Buffer is too short */
+    if (unlikely(ret > op_info->dst_len)) {
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    op_info->dst_len = ret;
+
     return VIRTIO_CRYPTO_OK;
 }
 
+static int cryptodev_builtin_operation(
+                 CryptoDevBackend *backend,
+                 CryptoDevBackendOpInfo *op_info,
+                 uint32_t queue_index, Error **errp)
+{
+    CryptoDevBackendBuiltin *builtin =
+                      CRYPTODEV_BACKEND_BUILTIN(backend);
+    CryptoDevBackendBuiltinSession *sess;
+    CryptoDevBackendSymOpInfo *sym_op_info;
+    CryptoDevBackendAsymOpInfo *asym_op_info;
+    enum CryptoDevBackendAlgType algtype = op_info->algtype;
+    int ret = -VIRTIO_CRYPTO_ERR;
+
+    if (op_info->session_id >= MAX_NUM_SESSIONS ||
+              builtin->sessions[op_info->session_id] == NULL) {
+        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
+                   op_info->session_id);
+        return -VIRTIO_CRYPTO_INVSESS;
+    }
+
+    sess = builtin->sessions[op_info->session_id];
+    if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
+        sym_op_info = op_info->u.sym_op_info;
+        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
+    } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
+        asym_op_info = op_info->u.asym_op_info;
+        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
+                                               asym_op_info, errp);
+    }
+
+    return ret;
+}
+
 static void cryptodev_builtin_cleanup(
              CryptoDevBackend *backend,
              Error **errp)
@@ -348,7 +542,7 @@ static void cryptodev_builtin_cleanup(
 
     for (i = 0; i < MAX_NUM_SESSIONS; i++) {
         if (builtin->sessions[i] != NULL) {
-            cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort);
+            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
         }
     }
 
@@ -370,9 +564,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void *data)
 
     bc->init = cryptodev_builtin_init;
     bc->cleanup = cryptodev_builtin_cleanup;
-    bc->create_session = cryptodev_builtin_sym_create_session;
-    bc->close_session = cryptodev_builtin_sym_close_session;
-    bc->do_sym_op = cryptodev_builtin_sym_operation;
+    bc->create_session = cryptodev_builtin_create_session;
+    bc->close_session = cryptodev_builtin_close_session;
+    bc->do_op = cryptodev_builtin_operation;
 }
 
 static const TypeInfo cryptodev_builtin_info = {
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index bedb452474..5443a59153 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -259,7 +259,33 @@ static int64_t cryptodev_vhost_user_sym_create_session(
     return -1;
 }
 
-static int cryptodev_vhost_user_sym_close_session(
+static int64_t cryptodev_vhost_user_create_session(
+           CryptoDevBackend *backend,
+           CryptoDevBackendSessionInfo *sess_info,
+           uint32_t queue_index, Error **errp)
+{
+    uint32_t op_code = sess_info->op_code;
+    CryptoDevBackendSymSessionInfo *sym_sess_info;
+
+    switch (op_code) {
+    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
+    case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
+    case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
+    case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
+        sym_sess_info = &sess_info->u.sym_sess_info;
+        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
+                   queue_index, errp);
+    default:
+        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+                   sess_info->op_code);
+        return -1;
+
+    }
+
+    return -1;
+}
+
+static int cryptodev_vhost_user_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
            uint32_t queue_index, Error **errp)
@@ -351,9 +377,9 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)
 
     bc->init = cryptodev_vhost_user_init;
     bc->cleanup = cryptodev_vhost_user_cleanup;
-    bc->create_session = cryptodev_vhost_user_sym_create_session;
-    bc->close_session = cryptodev_vhost_user_sym_close_session;
-    bc->do_sym_op = NULL;
+    bc->create_session = cryptodev_vhost_user_create_session;
+    bc->close_session = cryptodev_vhost_user_close_session;
+    bc->do_op = NULL;
 
     object_class_property_add_str(oc, "chardev",
                                   cryptodev_vhost_user_get_chardev,
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
index 2b105e433c..33eb4e1a70 100644
--- a/backends/cryptodev.c
+++ b/backends/cryptodev.c
@@ -72,9 +72,9 @@ void cryptodev_backend_cleanup(
     }
 }
 
-int64_t cryptodev_backend_sym_create_session(
+int64_t cryptodev_backend_create_session(
            CryptoDevBackend *backend,
-           CryptoDevBackendSymSessionInfo *sess_info,
+           CryptoDevBackendSessionInfo *sess_info,
            uint32_t queue_index, Error **errp)
 {
     CryptoDevBackendClass *bc =
@@ -87,7 +87,7 @@ int64_t cryptodev_backend_sym_create_session(
     return -1;
 }
 
-int cryptodev_backend_sym_close_session(
+int cryptodev_backend_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
            uint32_t queue_index, Error **errp)
@@ -102,16 +102,16 @@ int cryptodev_backend_sym_close_session(
     return -1;
 }
 
-static int cryptodev_backend_sym_operation(
+static int cryptodev_backend_operation(
                  CryptoDevBackend *backend,
-                 CryptoDevBackendSymOpInfo *op_info,
+                 CryptoDevBackendOpInfo *op_info,
                  uint32_t queue_index, Error **errp)
 {
     CryptoDevBackendClass *bc =
                       CRYPTODEV_BACKEND_GET_CLASS(backend);
 
-    if (bc->do_sym_op) {
-        return bc->do_sym_op(backend, op_info, queue_index, errp);
+    if (bc->do_op) {
+        return bc->do_op(backend, op_info, queue_index, errp);
     }
 
     return -VIRTIO_CRYPTO_ERR;
@@ -123,20 +123,18 @@ int cryptodev_backend_crypto_operation(
                  uint32_t queue_index, Error **errp)
 {
     VirtIOCryptoReq *req = opaque;
+    CryptoDevBackendOpInfo *op_info = &req->op_info;
+    enum CryptoDevBackendAlgType algtype = req->flags;
 
-    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
-        CryptoDevBackendSymOpInfo *op_info;
-        op_info = req->u.sym_op_info;
-
-        return cryptodev_backend_sym_operation(backend,
-                         op_info, queue_index, errp);
-    } else {
+    if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
+        && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
         error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
-                   req->flags);
-       return -VIRTIO_CRYPTO_NOTSUPP;
+                   algtype);
+
+        return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    return -VIRTIO_CRYPTO_ERR;
+    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
 }
 
 static void
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index dcd80b904d..a98105d9e9 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -83,7 +83,8 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
                struct iovec *iov, unsigned int out_num)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
-    CryptoDevBackendSymSessionInfo info;
+    CryptoDevBackendSessionInfo info;
+    CryptoDevBackendSymSessionInfo *sym_info;
     int64_t session_id;
     int queue_index;
     uint32_t op_type;
@@ -92,11 +93,13 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
 
     memset(&info, 0, sizeof(info));
     op_type = ldl_le_p(&sess_req->op_type);
-    info.op_type = op_type;
     info.op_code = opcode;
 
+    sym_info = &info.u.sym_sess_info;
+    sym_info->op_type = op_type;
+
     if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
-        ret = virtio_crypto_cipher_session_helper(vdev, &info,
+        ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
                            &sess_req->u.cipher.para,
                            &iov, &out_num);
         if (ret < 0) {
@@ -105,47 +108,47 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
     } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
         size_t s;
         /* cipher part */
-        ret = virtio_crypto_cipher_session_helper(vdev, &info,
+        ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
                            &sess_req->u.chain.para.cipher_param,
                            &iov, &out_num);
         if (ret < 0) {
             goto err;
         }
         /* hash part */
-        info.alg_chain_order = ldl_le_p(
+        sym_info->alg_chain_order = ldl_le_p(
                                      &sess_req->u.chain.para.alg_chain_order);
-        info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
-        info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
-        if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
-            info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
-            info.auth_key_len = ldl_le_p(
+        sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
+        sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
+        if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
+            sym_info->hash_alg =
+                ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
+            sym_info->auth_key_len = ldl_le_p(
                              &sess_req->u.chain.para.u.mac_param.auth_key_len);
-            info.hash_result_len = ldl_le_p(
+            sym_info->hash_result_len = ldl_le_p(
                            &sess_req->u.chain.para.u.mac_param.hash_result_len);
-            if (info.auth_key_len > vcrypto->conf.max_auth_key_len) {
+            if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
                 error_report("virtio-crypto length of auth key is too big: %u",
-                             info.auth_key_len);
+                             sym_info->auth_key_len);
                 ret = -VIRTIO_CRYPTO_ERR;
                 goto err;
             }
             /* get auth key */
-            if (info.auth_key_len > 0) {
-                DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len);
-                info.auth_key = g_malloc(info.auth_key_len);
-                s = iov_to_buf(iov, out_num, 0, info.auth_key,
-                               info.auth_key_len);
-                if (unlikely(s != info.auth_key_len)) {
+            if (sym_info->auth_key_len > 0) {
+                sym_info->auth_key = g_malloc(sym_info->auth_key_len);
+                s = iov_to_buf(iov, out_num, 0, sym_info->auth_key,
+                               sym_info->auth_key_len);
+                if (unlikely(s != sym_info->auth_key_len)) {
                     virtio_error(vdev,
                           "virtio-crypto authenticated key incorrect");
                     ret = -EFAULT;
                     goto err;
                 }
-                iov_discard_front(&iov, &out_num, info.auth_key_len);
+                iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
             }
-        } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
-            info.hash_alg = ldl_le_p(
+        } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
+            sym_info->hash_alg = ldl_le_p(
                              &sess_req->u.chain.para.u.hash_param.algo);
-            info.hash_result_len = ldl_le_p(
+            sym_info->hash_result_len = ldl_le_p(
                         &sess_req->u.chain.para.u.hash_param.hash_result_len);
         } else {
             /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
@@ -161,13 +164,10 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
     }
 
     queue_index = virtio_crypto_vq2q(queue_id);
-    session_id = cryptodev_backend_sym_create_session(
+    session_id = cryptodev_backend_create_session(
                                      vcrypto->cryptodev,
                                      &info, queue_index, &local_err);
     if (session_id >= 0) {
-        DPRINTF("create session_id=%" PRIu64 " successfully\n",
-                session_id);
-
         ret = session_id;
     } else {
         if (local_err) {
@@ -177,11 +177,78 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
     }
 
 err:
-    g_free(info.cipher_key);
-    g_free(info.auth_key);
+    g_free(sym_info->cipher_key);
+    g_free(sym_info->auth_key);
     return ret;
 }
 
+static int64_t
+virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
+               struct virtio_crypto_akcipher_create_session_req *sess_req,
+               uint32_t queue_id, uint32_t opcode,
+               struct iovec *iov, unsigned int out_num)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+    CryptoDevBackendSessionInfo info = {0};
+    CryptoDevBackendAsymSessionInfo *asym_info;
+    int64_t session_id;
+    int queue_index;
+    uint32_t algo, keytype, keylen;
+    g_autofree uint8_t *key = NULL;
+    Error *local_err = NULL;
+
+    algo = ldl_le_p(&sess_req->para.algo);
+    keytype = ldl_le_p(&sess_req->para.keytype);
+    keylen = ldl_le_p(&sess_req->para.keylen);
+
+    if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
+         && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {
+        error_report("unsupported asym keytype: %d", keytype);
+        return -VIRTIO_CRYPTO_NOTSUPP;
+    }
+
+    if (keylen) {
+        key = g_malloc(keylen);
+        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+            virtio_error(vdev, "virtio-crypto asym key incorrect");
+            return -EFAULT;
+        }
+        iov_discard_front(&iov, &out_num, keylen);
+    }
+
+    info.op_code = opcode;
+    asym_info = &info.u.asym_sess_info;
+    asym_info->algo = algo;
+    asym_info->keytype = keytype;
+    asym_info->keylen = keylen;
+    asym_info->key = key;
+    switch (asym_info->algo) {
+    case VIRTIO_CRYPTO_AKCIPHER_RSA:
+        asym_info->u.rsa.padding_algo =
+            ldl_le_p(&sess_req->para.u.rsa.padding_algo);
+        asym_info->u.rsa.hash_algo =
+            ldl_le_p(&sess_req->para.u.rsa.hash_algo);
+        break;
+
+    /* TODO DSA&ECDSA handling */
+
+    default:
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    queue_index = virtio_crypto_vq2q(queue_id);
+    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
+                     queue_index, &local_err);
+    if (session_id < 0) {
+        if (local_err) {
+            error_report_err(local_err);
+        }
+        return -VIRTIO_CRYPTO_ERR;
+    }
+
+    return session_id;
+}
+
 static uint8_t
 virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
          struct virtio_crypto_destroy_session_req *close_sess_req,
@@ -195,7 +262,7 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
     session_id = ldq_le_p(&close_sess_req->session_id);
     DPRINTF("close session, id=%" PRIu64 "\n", session_id);
 
-    ret = cryptodev_backend_sym_close_session(
+    ret = cryptodev_backend_close_session(
               vcrypto->cryptodev, session_id, queue_id, &local_err);
     if (ret == 0) {
         status = VIRTIO_CRYPTO_OK;
@@ -260,13 +327,22 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         opcode = ldl_le_p(&ctrl.header.opcode);
         queue_id = ldl_le_p(&ctrl.header.queue_id);
 
+        memset(&input, 0, sizeof(input));
         switch (opcode) {
         case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
-            memset(&input, 0, sizeof(input));
             session_id = virtio_crypto_create_sym_session(vcrypto,
                              &ctrl.u.sym_create_session,
                              queue_id, opcode,
                              out_iov, out_num);
+            goto check_session;
+
+        case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+            session_id = virtio_crypto_create_asym_session(vcrypto,
+                             &ctrl.u.akcipher_create_session,
+                             queue_id, opcode,
+                             out_iov, out_num);
+
+check_session:
             /* Serious errors, need to reset virtio crypto device */
             if (session_id == -EFAULT) {
                 virtqueue_detach_element(vq, elem, 0);
@@ -290,10 +366,12 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
             virtqueue_push(vq, elem, sizeof(input));
             virtio_notify(vdev, vq);
             break;
+
         case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
         case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
         case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
         case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
+        case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
             status = virtio_crypto_handle_close_session(vcrypto,
                    &ctrl.u.destroy_session, queue_id);
             /* The status only occupy one byte, we can directly use it */
@@ -311,7 +389,6 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
         default:
             error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
-            memset(&input, 0, sizeof(input));
             stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
             s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
             if (unlikely(s != sizeof(input))) {
@@ -339,28 +416,39 @@ static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq,
     req->in_num = 0;
     req->in_len = 0;
     req->flags = CRYPTODEV_BACKEND_ALG__MAX;
-    req->u.sym_op_info = NULL;
+    memset(&req->op_info, 0x00, sizeof(req->op_info));
 }
 
 static void virtio_crypto_free_request(VirtIOCryptoReq *req)
 {
-    if (req) {
-        if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
-            size_t max_len;
-            CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info;
-
-            max_len = op_info->iv_len +
-                      op_info->aad_len +
-                      op_info->src_len +
-                      op_info->dst_len +
-                      op_info->digest_result_len;
-
-            /* Zeroize and free request data structure */
-            memset(op_info, 0, sizeof(*op_info) + max_len);
+    if (!req) {
+        return;
+    }
+
+    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
+        size_t max_len;
+        CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info;
+
+        max_len = op_info->iv_len +
+                  op_info->aad_len +
+                  op_info->src_len +
+                  op_info->dst_len +
+                  op_info->digest_result_len;
+
+        /* Zeroize and free request data structure */
+        memset(op_info, 0, sizeof(*op_info) + max_len);
+        g_free(op_info);
+    } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
+        CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info;
+        if (op_info) {
+            g_free(op_info->src);
+            g_free(op_info->dst);
+            memset(op_info, 0, sizeof(*op_info));
             g_free(op_info);
         }
-        g_free(req);
     }
+
+    g_free(req);
 }
 
 static void
@@ -397,6 +485,35 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev,
     }
 }
 
+static void
+virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
+        VirtIOCryptoReq *req, int32_t status,
+        CryptoDevBackendAsymOpInfo *asym_op_info)
+{
+    size_t s, len;
+
+    if (status != VIRTIO_CRYPTO_OK) {
+        return;
+    }
+
+    len = asym_op_info->dst_len;
+    if (!len) {
+        return;
+    }
+
+    s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len);
+    if (s != len) {
+        virtio_error(vdev, "virtio-crypto asym dest data incorrect");
+        return;
+    }
+
+    iov_discard_front(&req->in_iov, &req->in_num, len);
+
+    /* For akcipher, dst_len may be changed after operation */
+    req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
+}
+
+
 static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
 {
     VirtIOCrypto *vcrypto = req->vcrypto;
@@ -404,7 +521,10 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
 
     if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
         virtio_crypto_sym_input_data_helper(vdev, req, status,
-                                            req->u.sym_op_info);
+                                            req->op_info.u.sym_op_info);
+    } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
+        virtio_crypto_akcipher_input_data_helper(vdev, req, status,
+                                             req->op_info.u.asym_op_info);
     }
     stb_p(&req->in->status, status);
     virtqueue_push(req->vq, &req->elem, req->in_len);
@@ -543,41 +663,100 @@ err:
 static int
 virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto,
                struct virtio_crypto_sym_data_req *req,
-               CryptoDevBackendSymOpInfo **sym_op_info,
+               CryptoDevBackendOpInfo *op_info,
                struct iovec *iov, unsigned int out_num)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+    CryptoDevBackendSymOpInfo *sym_op_info;
     uint32_t op_type;
-    CryptoDevBackendSymOpInfo *op_info;
 
     op_type = ldl_le_p(&req->op_type);
-
     if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
-        op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para,
+        sym_op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para,
                                               NULL, iov, out_num);
-        if (!op_info) {
+        if (!sym_op_info) {
             return -EFAULT;
         }
-        op_info->op_type = op_type;
     } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
-        op_info = virtio_crypto_sym_op_helper(vdev, NULL,
+        sym_op_info = virtio_crypto_sym_op_helper(vdev, NULL,
                                               &req->u.chain.para,
                                               iov, out_num);
-        if (!op_info) {
+        if (!sym_op_info) {
             return -EFAULT;
         }
-        op_info->op_type = op_type;
     } else {
         /* VIRTIO_CRYPTO_SYM_OP_NONE */
         error_report("virtio-crypto unsupported cipher type");
         return -VIRTIO_CRYPTO_NOTSUPP;
     }
 
-    *sym_op_info = op_info;
+    sym_op_info->op_type = op_type;
+    op_info->u.sym_op_info = sym_op_info;
 
     return 0;
 }
 
+static int
+virtio_crypto_handle_asym_req(VirtIOCrypto *vcrypto,
+               struct virtio_crypto_akcipher_data_req *req,
+               CryptoDevBackendOpInfo *op_info,
+               struct iovec *iov, unsigned int out_num)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+    CryptoDevBackendAsymOpInfo *asym_op_info;
+    uint32_t src_len;
+    uint32_t dst_len;
+    uint32_t len;
+    uint8_t *src = NULL;
+    uint8_t *dst = NULL;
+
+    asym_op_info = g_malloc0(sizeof(CryptoDevBackendAsymOpInfo));
+    src_len = ldl_le_p(&req->para.src_data_len);
+    dst_len = ldl_le_p(&req->para.dst_data_len);
+
+    if (src_len > 0) {
+        src = g_malloc0(src_len);
+        len = iov_to_buf(iov, out_num, 0, src, src_len);
+        if (unlikely(len != src_len)) {
+            virtio_error(vdev, "virtio-crypto asym src data incorrect"
+                         "expected %u, actual %u", src_len, len);
+            goto err;
+        }
+
+        iov_discard_front(&iov, &out_num, src_len);
+    }
+
+    if (dst_len > 0) {
+        dst = g_malloc0(dst_len);
+
+        if (op_info->op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+            len = iov_to_buf(iov, out_num, 0, dst, dst_len);
+            if (unlikely(len != dst_len)) {
+                virtio_error(vdev, "virtio-crypto asym dst data incorrect"
+                             "expected %u, actual %u", dst_len, len);
+                goto err;
+            }
+
+            iov_discard_front(&iov, &out_num, dst_len);
+        }
+    }
+
+    asym_op_info->src_len = src_len;
+    asym_op_info->dst_len = dst_len;
+    asym_op_info->src = src;
+    asym_op_info->dst = dst;
+    op_info->u.asym_op_info = asym_op_info;
+
+    return 0;
+
+ err:
+    g_free(asym_op_info);
+    g_free(src);
+    g_free(dst);
+
+    return -EFAULT;
+}
+
 static int
 virtio_crypto_handle_request(VirtIOCryptoReq *request)
 {
@@ -595,8 +774,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
     unsigned out_num;
     uint32_t opcode;
     uint8_t status = VIRTIO_CRYPTO_ERR;
-    uint64_t session_id;
-    CryptoDevBackendSymOpInfo *sym_op_info = NULL;
+    CryptoDevBackendOpInfo *op_info = &request->op_info;
     Error *local_err = NULL;
 
     if (elem->out_num < 1 || elem->in_num < 1) {
@@ -639,15 +817,28 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
     request->in_iov = in_iov;
 
     opcode = ldl_le_p(&req.header.opcode);
-    session_id = ldq_le_p(&req.header.session_id);
+    op_info->session_id = ldq_le_p(&req.header.session_id);
+    op_info->op_code = opcode;
 
     switch (opcode) {
     case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
     case VIRTIO_CRYPTO_CIPHER_DECRYPT:
+        op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_SYM;
         ret = virtio_crypto_handle_sym_req(vcrypto,
-                         &req.u.sym_req,
-                         &sym_op_info,
+                         &req.u.sym_req, op_info,
+                         out_iov, out_num);
+        goto check_result;
+
+    case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+    case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+    case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+    case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+        op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_ASYM;
+        ret = virtio_crypto_handle_asym_req(vcrypto,
+                         &req.u.akcipher_req, op_info,
                          out_iov, out_num);
+
+check_result:
         /* Serious errors, need to reset virtio crypto device */
         if (ret == -EFAULT) {
             return -1;
@@ -655,11 +846,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
             virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
             virtio_crypto_free_request(request);
         } else {
-            sym_op_info->session_id = session_id;
 
             /* Set request's parameter */
-            request->flags = CRYPTODEV_BACKEND_ALG_SYM;
-            request->u.sym_op_info = sym_op_info;
             ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
                                     request, queue_index, &local_err);
             if (ret < 0) {
@@ -674,6 +862,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
             virtio_crypto_free_request(request);
         }
         break;
+
     case VIRTIO_CRYPTO_HASH:
     case VIRTIO_CRYPTO_MAC:
     case VIRTIO_CRYPTO_AEAD_ENCRYPT:
@@ -779,6 +968,7 @@ static void virtio_crypto_init_config(VirtIODevice *vdev)
     vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l;
     vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h;
     vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo;
+    vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo;
     vcrypto->conf.max_cipher_key_len =
                   vcrypto->conf.cryptodev->conf.max_cipher_key_len;
     vcrypto->conf.max_auth_key_len =
@@ -891,6 +1081,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config)
     stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len);
     stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len);
     stq_le_p(&crypto_cfg.max_size, c->conf.max_size);
+    stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo);
 
     memcpy(config, &crypto_cfg, c->config_size);
 }
diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h
index a2228d7b2e..348749f5d5 100644
--- a/include/hw/virtio/virtio-crypto.h
+++ b/include/hw/virtio/virtio-crypto.h
@@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf {
     uint32_t mac_algo_l;
     uint32_t mac_algo_h;
     uint32_t aead_algo;
+    uint32_t akcipher_algo;
 
     /* Maximum length of cipher key */
     uint32_t max_cipher_key_len;
@@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq {
     size_t in_len;
     VirtQueue *vq;
     struct VirtIOCrypto *vcrypto;
-    union {
-        CryptoDevBackendSymOpInfo *sym_op_info;
-    } u;
+    CryptoDevBackendOpInfo op_info;
 } VirtIOCryptoReq;
 
 typedef struct VirtIOCryptoQueue {
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index f4d4057d4d..37c3a360fd 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -50,13 +50,13 @@ typedef struct CryptoDevBackendClient
 
 enum CryptoDevBackendAlgType {
     CRYPTODEV_BACKEND_ALG_SYM,
+    CRYPTODEV_BACKEND_ALG_ASYM,
     CRYPTODEV_BACKEND_ALG__MAX,
 };
 
 /**
  * CryptoDevBackendSymSessionInfo:
  *
- * @op_code: operation code (refer to virtio_crypto.h)
  * @cipher_alg: algorithm type of CIPHER
  * @key_len: byte length of cipher key
  * @hash_alg: algorithm type of HASH/MAC
@@ -74,7 +74,6 @@ enum CryptoDevBackendAlgType {
  */
 typedef struct CryptoDevBackendSymSessionInfo {
     /* corresponding with virtio crypto spec */
-    uint32_t op_code;
     uint32_t cipher_alg;
     uint32_t key_len;
     uint32_t hash_alg;
@@ -89,11 +88,36 @@ typedef struct CryptoDevBackendSymSessionInfo {
     uint8_t *auth_key;
 } CryptoDevBackendSymSessionInfo;
 
+/**
+ * CryptoDevBackendAsymSessionInfo:
+ */
+typedef struct CryptoDevBackendRsaPara {
+    uint32_t padding_algo;
+    uint32_t hash_algo;
+} CryptoDevBackendRsaPara;
+
+typedef struct CryptoDevBackendAsymSessionInfo {
+    /* corresponding with virtio crypto spec */
+    uint32_t algo;
+    uint32_t keytype;
+    uint32_t keylen;
+    uint8_t *key;
+    union {
+        CryptoDevBackendRsaPara rsa;
+    } u;
+} CryptoDevBackendAsymSessionInfo;
+
+typedef struct CryptoDevBackendSessionInfo {
+    uint32_t op_code;
+    union {
+        CryptoDevBackendSymSessionInfo sym_sess_info;
+        CryptoDevBackendAsymSessionInfo asym_sess_info;
+    } u;
+} CryptoDevBackendSessionInfo;
+
 /**
  * CryptoDevBackendSymOpInfo:
  *
- * @session_id: session index which was previously
- *              created by cryptodev_backend_sym_create_session()
  * @aad_len: byte length of additional authenticated data
  * @iv_len: byte length of initialization vector or counter
  * @src_len: byte length of source data
@@ -119,7 +143,6 @@ typedef struct CryptoDevBackendSymSessionInfo {
  *
  */
 typedef struct CryptoDevBackendSymOpInfo {
-    uint64_t session_id;
     uint32_t aad_len;
     uint32_t iv_len;
     uint32_t src_len;
@@ -138,6 +161,33 @@ typedef struct CryptoDevBackendSymOpInfo {
     uint8_t data[];
 } CryptoDevBackendSymOpInfo;
 
+
+/**
+ * CryptoDevBackendAsymOpInfo:
+ *
+ * @src_len: byte length of source data
+ * @dst_len: byte length of destination data
+ * @src: point to the source data
+ * @dst: point to the destination data
+ *
+ */
+typedef struct CryptoDevBackendAsymOpInfo {
+    uint32_t src_len;
+    uint32_t dst_len;
+    uint8_t *src;
+    uint8_t *dst;
+} CryptoDevBackendAsymOpInfo;
+
+typedef struct CryptoDevBackendOpInfo {
+    enum CryptoDevBackendAlgType algtype;
+    uint32_t op_code;
+    uint64_t session_id;
+    union {
+        CryptoDevBackendSymOpInfo *sym_op_info;
+        CryptoDevBackendAsymOpInfo *asym_op_info;
+    } u;
+} CryptoDevBackendOpInfo;
+
 struct CryptoDevBackendClass {
     ObjectClass parent_class;
 
@@ -145,13 +195,13 @@ struct CryptoDevBackendClass {
     void (*cleanup)(CryptoDevBackend *backend, Error **errp);
 
     int64_t (*create_session)(CryptoDevBackend *backend,
-                       CryptoDevBackendSymSessionInfo *sess_info,
+                       CryptoDevBackendSessionInfo *sess_info,
                        uint32_t queue_index, Error **errp);
     int (*close_session)(CryptoDevBackend *backend,
                            uint64_t session_id,
                            uint32_t queue_index, Error **errp);
-    int (*do_sym_op)(CryptoDevBackend *backend,
-                     CryptoDevBackendSymOpInfo *op_info,
+    int (*do_op)(CryptoDevBackend *backend,
+                     CryptoDevBackendOpInfo *op_info,
                      uint32_t queue_index, Error **errp);
 };
 
@@ -190,6 +240,7 @@ struct CryptoDevBackendConf {
     uint32_t mac_algo_l;
     uint32_t mac_algo_h;
     uint32_t aead_algo;
+    uint32_t akcipher_algo;
     /* Maximum length of cipher key */
     uint32_t max_cipher_key_len;
     /* Maximum length of authenticated key */
@@ -247,34 +298,34 @@ void cryptodev_backend_cleanup(
            Error **errp);
 
 /**
- * cryptodev_backend_sym_create_session:
+ * cryptodev_backend_create_session:
  * @backend: the cryptodev backend object
  * @sess_info: parameters needed by session creating
  * @queue_index: queue index of cryptodev backend client
  * @errp: pointer to a NULL-initialized error object
  *
- * Create a session for symmetric algorithms
+ * Create a session for symmetric/symmetric algorithms
  *
  * Returns: session id on success, or -1 on error
  */
-int64_t cryptodev_backend_sym_create_session(
+int64_t cryptodev_backend_create_session(
            CryptoDevBackend *backend,
-           CryptoDevBackendSymSessionInfo *sess_info,
+           CryptoDevBackendSessionInfo *sess_info,
            uint32_t queue_index, Error **errp);
 
 /**
- * cryptodev_backend_sym_close_session:
+ * cryptodev_backend_close_session:
  * @backend: the cryptodev backend object
  * @session_id: the session id
  * @queue_index: queue index of cryptodev backend client
  * @errp: pointer to a NULL-initialized error object
  *
- * Close a session for symmetric algorithms which was previously
- * created by cryptodev_backend_sym_create_session()
+ * Close a session for which was previously
+ * created by cryptodev_backend_create_session()
  *
  * Returns: 0 on success, or Negative on error
  */
-int cryptodev_backend_sym_close_session(
+int cryptodev_backend_close_session(
            CryptoDevBackend *backend,
            uint64_t session_id,
            uint32_t queue_index, Error **errp);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* PING: [PATCH v5 9/9] crypto: Introduce RSA algorithm
  2022-04-28 13:59 ` [PATCH v5 9/9] crypto: Introduce RSA algorithm zhenwei pi
@ 2022-05-09  2:17   ` zhenwei pi
  0 siblings, 0 replies; 23+ messages in thread
From: zhenwei pi @ 2022-05-09  2:17 UTC (permalink / raw)
  To: arei.gonglei
  Cc: qemu-devel, berrange, mst, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

Hi, Lei

I would appreciate it if you could review this patch!

On 4/28/22 21:59, zhenwei pi wrote:
> There are two parts in this patch:
> 1, support akcipher service by cryptodev-builtin driver
> 2, virtio-crypto driver supports akcipher service
> 
> In principle, we should separate this into two patches, to avoid
> compiling error, merge them into one.
> 
> Then virtio-crypto gets request from guest side, and forwards the
> request to builtin driver to handle it.
> 
> Test with a guest linux:
> 1, The self-test framework of crypto layer works fine in guest kernel
> 2, Test with Linux guest(with asym support), the following script
> test(note that pkey_XXX is supported only in a newer version of keyutils):
>    - both public key & private key
>    - create/close session
>    - encrypt/decrypt/sign/verify basic driver operation
>    - also test with kernel crypto layer(pkey add/query)
> 
> All the cases work fine.
> 
> Run script in guest:
> rm -rf *.der *.pem *.pfx
> modprobe pkcs8_key_parser # if CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
> rm -rf /tmp/data
> dd if=/dev/random of=/tmp/data count=1 bs=20
> 
> openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj "/C=CN/ST=BJ/L=HD/O=qemu/OU=dev/CN=qemu/emailAddress=qemu@qemu.org"
> openssl pkcs8 -in key.pem -topk8 -nocrypt -outform DER -out key.der
> openssl x509 -in cert.pem -inform PEM -outform DER -out cert.der
> 
> PRIV_KEY_ID=`cat key.der | keyctl padd asymmetric test_priv_key @s`
> echo "priv key id = "$PRIV_KEY_ID
> PUB_KEY_ID=`cat cert.der | keyctl padd asymmetric test_pub_key @s`
> echo "pub key id = "$PUB_KEY_ID
> 
> keyctl pkey_query $PRIV_KEY_ID 0
> keyctl pkey_query $PUB_KEY_ID 0
> 
> echo "Enc with priv key..."
> keyctl pkey_encrypt $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.priv
> echo "Dec with pub key..."
> keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.priv enc=pkcs1 >/tmp/dec
> cmp /tmp/data /tmp/dec
> 
> echo "Sign with priv key..."
> keyctl pkey_sign $PRIV_KEY_ID 0 /tmp/data enc=pkcs1 hash=sha1 > /tmp/sig
> echo "Verify with pub key..."
> keyctl pkey_verify $PRIV_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1
> 
> echo "Enc with pub key..."
> keyctl pkey_encrypt $PUB_KEY_ID 0 /tmp/data enc=pkcs1 >/tmp/enc.pub
> echo "Dec with priv key..."
> keyctl pkey_decrypt $PRIV_KEY_ID 0 /tmp/enc.pub enc=pkcs1 >/tmp/dec
> cmp /tmp/data /tmp/dec
> 
> echo "Verify with pub key..."
> keyctl pkey_verify $PUB_KEY_ID 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1
> 
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> Signed-off-by: lei he <helei.sig11@bytedance.com
> ---
>   backends/cryptodev-builtin.c      | 272 +++++++++++++++++++++----
>   backends/cryptodev-vhost-user.c   |  34 +++-
>   backends/cryptodev.c              |  32 ++-
>   hw/virtio/virtio-crypto.c         | 323 ++++++++++++++++++++++++------
>   include/hw/virtio/virtio-crypto.h |   5 +-
>   include/sysemu/cryptodev.h        |  83 ++++++--
>   6 files changed, 604 insertions(+), 145 deletions(-)
> 
> diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
> index 0671bf9f3e..388aedd8df 100644
> --- a/backends/cryptodev-builtin.c
> +++ b/backends/cryptodev-builtin.c
> @@ -26,6 +26,7 @@
>   #include "qapi/error.h"
>   #include "standard-headers/linux/virtio_crypto.h"
>   #include "crypto/cipher.h"
> +#include "crypto/akcipher.h"
>   #include "qom/object.h"
>   
>   
> @@ -41,11 +42,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN)
>   typedef struct CryptoDevBackendBuiltinSession {
>       QCryptoCipher *cipher;
>       uint8_t direction; /* encryption or decryption */
> -    uint8_t type; /* cipher? hash? aead? */
> +    uint8_t type; /* cipher? hash? aead? akcipher? */
> +    QCryptoAkCipher *akcipher;
>       QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
>   } CryptoDevBackendBuiltinSession;
>   
> -/* Max number of symmetric sessions */
> +/* Max number of symmetric/asymmetric sessions */
>   #define MAX_NUM_SESSIONS 256
>   
>   #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN    512
> @@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
>       backend->conf.crypto_services =
>                            1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
>                            1u << VIRTIO_CRYPTO_SERVICE_HASH |
> -                         1u << VIRTIO_CRYPTO_SERVICE_MAC;
> +                         1u << VIRTIO_CRYPTO_SERVICE_MAC |
> +                         1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
>       backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
>       backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
> +    backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
>       /*
>        * Set the Maximum length of crypto request.
>        * Why this value? Just avoid to overflow when
>        * memory allocation for each crypto request.
>        */
> -    backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
> +    backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
>       backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
>       backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
>   
> @@ -148,6 +152,53 @@ err:
>      return -1;
>   }
>   
> +static int cryptodev_builtin_get_rsa_hash_algo(
> +    int virtio_rsa_hash, Error **errp)
> +{
> +    switch (virtio_rsa_hash) {
> +    case VIRTIO_CRYPTO_RSA_MD5:
> +        return QCRYPTO_HASH_ALG_MD5;
> +
> +    case VIRTIO_CRYPTO_RSA_SHA1:
> +        return QCRYPTO_HASH_ALG_SHA1;
> +
> +    case VIRTIO_CRYPTO_RSA_SHA256:
> +        return QCRYPTO_HASH_ALG_SHA256;
> +
> +    case VIRTIO_CRYPTO_RSA_SHA512:
> +        return QCRYPTO_HASH_ALG_SHA512;
> +
> +    default:
> +        error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
> +        return -1;
> +    }
> +}
> +
> +static int cryptodev_builtin_set_rsa_options(
> +                    int virtio_padding_algo,
> +                    int virtio_hash_algo,
> +                    QCryptoAkCipherOptionsRSA *opt,
> +                    Error **errp)
> +{
> +    if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
> +        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
> +        opt->hash_alg =
> +            cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
> +        if (opt->hash_alg < 0) {
> +            return -1;
> +        }
> +        return 0;
> +    }
> +
> +    if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
> +        opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
> +        return 0;
> +    }
> +
> +    error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
> +    return -1;
> +}
> +
>   static int cryptodev_builtin_create_cipher_session(
>                       CryptoDevBackendBuiltin *builtin,
>                       CryptoDevBackendSymSessionInfo *sess_info,
> @@ -240,26 +291,89 @@ static int cryptodev_builtin_create_cipher_session(
>       return index;
>   }
>   
> -static int64_t cryptodev_builtin_sym_create_session(
> +static int cryptodev_builtin_create_akcipher_session(
> +                    CryptoDevBackendBuiltin *builtin,
> +                    CryptoDevBackendAsymSessionInfo *sess_info,
> +                    Error **errp)
> +{
> +    CryptoDevBackendBuiltinSession *sess;
> +    QCryptoAkCipher *akcipher;
> +    int index;
> +    QCryptoAkCipherKeyType type;
> +    QCryptoAkCipherOptions opts;
> +
> +    switch (sess_info->algo) {
> +    case VIRTIO_CRYPTO_AKCIPHER_RSA:
> +        opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
> +        if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo,
> +            sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) {
> +            return -1;
> +        }
> +        break;
> +
> +    /* TODO support DSA&ECDSA until qemu crypto framework support these */
> +
> +    default:
> +        error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo);
> +        return -1;
> +    }
> +
> +    switch (sess_info->keytype) {
> +    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> +        type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
> +        break;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> +        type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype);
> +        return -1;
> +    }
> +
> +    index = cryptodev_builtin_get_unused_session_index(builtin);
> +    if (index < 0) {
> +        error_setg(errp, "Total number of sessions created exceeds %u",
> +                   MAX_NUM_SESSIONS);
> +        return -1;
> +    }
> +
> +    akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key,
> +                                    sess_info->keylen, errp);
> +    if (!akcipher) {
> +        return -1;
> +    }
> +
> +    sess = g_new0(CryptoDevBackendBuiltinSession, 1);
> +    sess->akcipher = akcipher;
> +
> +    builtin->sessions[index] = sess;
> +
> +    return index;
> +}
> +
> +static int64_t cryptodev_builtin_create_session(
>              CryptoDevBackend *backend,
> -           CryptoDevBackendSymSessionInfo *sess_info,
> +           CryptoDevBackendSessionInfo *sess_info,
>              uint32_t queue_index, Error **errp)
>   {
>       CryptoDevBackendBuiltin *builtin =
>                         CRYPTODEV_BACKEND_BUILTIN(backend);
> -    int64_t session_id = -1;
> -    int ret;
> +    CryptoDevBackendSymSessionInfo *sym_sess_info;
> +    CryptoDevBackendAsymSessionInfo *asym_sess_info;
>   
>       switch (sess_info->op_code) {
>       case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> -        ret = cryptodev_builtin_create_cipher_session(
> -                           builtin, sess_info, errp);
> -        if (ret < 0) {
> -            return ret;
> -        } else {
> -            session_id = ret;
> -        }
> -        break;
> +        sym_sess_info = &sess_info->u.sym_sess_info;
> +        return cryptodev_builtin_create_cipher_session(
> +                           builtin, sym_sess_info, errp);
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> +        asym_sess_info = &sess_info->u.asym_sess_info;
> +        return cryptodev_builtin_create_akcipher_session(
> +                           builtin, asym_sess_info, errp);
> +
>       case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
>       case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
>       default:
> @@ -268,50 +382,44 @@ static int64_t cryptodev_builtin_sym_create_session(
>           return -1;
>       }
>   
> -    return session_id;
> +    return -1;
>   }
>   
> -static int cryptodev_builtin_sym_close_session(
> +static int cryptodev_builtin_close_session(
>              CryptoDevBackend *backend,
>              uint64_t session_id,
>              uint32_t queue_index, Error **errp)
>   {
>       CryptoDevBackendBuiltin *builtin =
>                         CRYPTODEV_BACKEND_BUILTIN(backend);
> +    CryptoDevBackendBuiltinSession *session;
>   
>       assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
>   
> -    qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
> -    g_free(builtin->sessions[session_id]);
> +    session = builtin->sessions[session_id];
> +    if (session->cipher) {
> +        qcrypto_cipher_free(session->cipher);
> +    } else if (session->akcipher) {
> +        qcrypto_akcipher_free(session->akcipher);
> +    }
> +
> +    g_free(session);
>       builtin->sessions[session_id] = NULL;
>       return 0;
>   }
>   
>   static int cryptodev_builtin_sym_operation(
> -                 CryptoDevBackend *backend,
> -                 CryptoDevBackendSymOpInfo *op_info,
> -                 uint32_t queue_index, Error **errp)
> +                 CryptoDevBackendBuiltinSession *sess,
> +                 CryptoDevBackendSymOpInfo *op_info, Error **errp)
>   {
> -    CryptoDevBackendBuiltin *builtin =
> -                      CRYPTODEV_BACKEND_BUILTIN(backend);
> -    CryptoDevBackendBuiltinSession *sess;
>       int ret;
>   
> -    if (op_info->session_id >= MAX_NUM_SESSIONS ||
> -              builtin->sessions[op_info->session_id] == NULL) {
> -        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> -                   op_info->session_id);
> -        return -VIRTIO_CRYPTO_INVSESS;
> -    }
> -
>       if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
>           error_setg(errp,
>                  "Algorithm chain is unsupported for cryptdoev-builtin");
>           return -VIRTIO_CRYPTO_NOTSUPP;
>       }
>   
> -    sess = builtin->sessions[op_info->session_id];
> -
>       if (op_info->iv_len > 0) {
>           ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
>                                      op_info->iv_len, errp);
> @@ -333,9 +441,95 @@ static int cryptodev_builtin_sym_operation(
>               return -VIRTIO_CRYPTO_ERR;
>           }
>       }
> +
> +    return VIRTIO_CRYPTO_OK;
> +}
> +
> +static int cryptodev_builtin_asym_operation(
> +                 CryptoDevBackendBuiltinSession *sess, uint32_t op_code,
> +                 CryptoDevBackendAsymOpInfo *op_info, Error **errp)
> +{
> +    int ret;
> +
> +    switch (op_code) {
> +    case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
> +        ret = qcrypto_akcipher_encrypt(sess->akcipher,
> +                                       op_info->src, op_info->src_len,
> +                                       op_info->dst, op_info->dst_len, errp);
> +        break;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
> +        ret = qcrypto_akcipher_decrypt(sess->akcipher,
> +                                       op_info->src, op_info->src_len,
> +                                       op_info->dst, op_info->dst_len, errp);
> +        break;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_SIGN:
> +        ret = qcrypto_akcipher_sign(sess->akcipher,
> +                                    op_info->src, op_info->src_len,
> +                                    op_info->dst, op_info->dst_len, errp);
> +        break;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
> +        ret = qcrypto_akcipher_verify(sess->akcipher,
> +                                      op_info->src, op_info->src_len,
> +                                      op_info->dst, op_info->dst_len, errp);
> +    break;
> +
> +    default:
> +        return -VIRTIO_CRYPTO_ERR;
> +    }
> +
> +    if (ret < 0) {
> +        if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
> +            return -VIRTIO_CRYPTO_KEY_REJECTED;
> +        }
> +        return -VIRTIO_CRYPTO_ERR;
> +    }
> +
> +    /* Buffer is too short */
> +    if (unlikely(ret > op_info->dst_len)) {
> +        return -VIRTIO_CRYPTO_ERR;
> +    }
> +
> +    op_info->dst_len = ret;
> +
>       return VIRTIO_CRYPTO_OK;
>   }
>   
> +static int cryptodev_builtin_operation(
> +                 CryptoDevBackend *backend,
> +                 CryptoDevBackendOpInfo *op_info,
> +                 uint32_t queue_index, Error **errp)
> +{
> +    CryptoDevBackendBuiltin *builtin =
> +                      CRYPTODEV_BACKEND_BUILTIN(backend);
> +    CryptoDevBackendBuiltinSession *sess;
> +    CryptoDevBackendSymOpInfo *sym_op_info;
> +    CryptoDevBackendAsymOpInfo *asym_op_info;
> +    enum CryptoDevBackendAlgType algtype = op_info->algtype;
> +    int ret = -VIRTIO_CRYPTO_ERR;
> +
> +    if (op_info->session_id >= MAX_NUM_SESSIONS ||
> +              builtin->sessions[op_info->session_id] == NULL) {
> +        error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> +                   op_info->session_id);
> +        return -VIRTIO_CRYPTO_INVSESS;
> +    }
> +
> +    sess = builtin->sessions[op_info->session_id];
> +    if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
> +        sym_op_info = op_info->u.sym_op_info;
> +        ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
> +    } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
> +        asym_op_info = op_info->u.asym_op_info;
> +        ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> +                                               asym_op_info, errp);
> +    }
> +
> +    return ret;
> +}
> +
>   static void cryptodev_builtin_cleanup(
>                CryptoDevBackend *backend,
>                Error **errp)
> @@ -348,7 +542,7 @@ static void cryptodev_builtin_cleanup(
>   
>       for (i = 0; i < MAX_NUM_SESSIONS; i++) {
>           if (builtin->sessions[i] != NULL) {
> -            cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort);
> +            cryptodev_builtin_close_session(backend, i, 0, &error_abort);
>           }
>       }
>   
> @@ -370,9 +564,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void *data)
>   
>       bc->init = cryptodev_builtin_init;
>       bc->cleanup = cryptodev_builtin_cleanup;
> -    bc->create_session = cryptodev_builtin_sym_create_session;
> -    bc->close_session = cryptodev_builtin_sym_close_session;
> -    bc->do_sym_op = cryptodev_builtin_sym_operation;
> +    bc->create_session = cryptodev_builtin_create_session;
> +    bc->close_session = cryptodev_builtin_close_session;
> +    bc->do_op = cryptodev_builtin_operation;
>   }
>   
>   static const TypeInfo cryptodev_builtin_info = {
> diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
> index bedb452474..5443a59153 100644
> --- a/backends/cryptodev-vhost-user.c
> +++ b/backends/cryptodev-vhost-user.c
> @@ -259,7 +259,33 @@ static int64_t cryptodev_vhost_user_sym_create_session(
>       return -1;
>   }
>   
> -static int cryptodev_vhost_user_sym_close_session(
> +static int64_t cryptodev_vhost_user_create_session(
> +           CryptoDevBackend *backend,
> +           CryptoDevBackendSessionInfo *sess_info,
> +           uint32_t queue_index, Error **errp)
> +{
> +    uint32_t op_code = sess_info->op_code;
> +    CryptoDevBackendSymSessionInfo *sym_sess_info;
> +
> +    switch (op_code) {
> +    case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> +    case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
> +    case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> +    case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
> +        sym_sess_info = &sess_info->u.sym_sess_info;
> +        return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
> +                   queue_index, errp);
> +    default:
> +        error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> +                   sess_info->op_code);
> +        return -1;
> +
> +    }
> +
> +    return -1;
> +}
> +
> +static int cryptodev_vhost_user_close_session(
>              CryptoDevBackend *backend,
>              uint64_t session_id,
>              uint32_t queue_index, Error **errp)
> @@ -351,9 +377,9 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)
>   
>       bc->init = cryptodev_vhost_user_init;
>       bc->cleanup = cryptodev_vhost_user_cleanup;
> -    bc->create_session = cryptodev_vhost_user_sym_create_session;
> -    bc->close_session = cryptodev_vhost_user_sym_close_session;
> -    bc->do_sym_op = NULL;
> +    bc->create_session = cryptodev_vhost_user_create_session;
> +    bc->close_session = cryptodev_vhost_user_close_session;
> +    bc->do_op = NULL;
>   
>       object_class_property_add_str(oc, "chardev",
>                                     cryptodev_vhost_user_get_chardev,
> diff --git a/backends/cryptodev.c b/backends/cryptodev.c
> index 2b105e433c..33eb4e1a70 100644
> --- a/backends/cryptodev.c
> +++ b/backends/cryptodev.c
> @@ -72,9 +72,9 @@ void cryptodev_backend_cleanup(
>       }
>   }
>   
> -int64_t cryptodev_backend_sym_create_session(
> +int64_t cryptodev_backend_create_session(
>              CryptoDevBackend *backend,
> -           CryptoDevBackendSymSessionInfo *sess_info,
> +           CryptoDevBackendSessionInfo *sess_info,
>              uint32_t queue_index, Error **errp)
>   {
>       CryptoDevBackendClass *bc =
> @@ -87,7 +87,7 @@ int64_t cryptodev_backend_sym_create_session(
>       return -1;
>   }
>   
> -int cryptodev_backend_sym_close_session(
> +int cryptodev_backend_close_session(
>              CryptoDevBackend *backend,
>              uint64_t session_id,
>              uint32_t queue_index, Error **errp)
> @@ -102,16 +102,16 @@ int cryptodev_backend_sym_close_session(
>       return -1;
>   }
>   
> -static int cryptodev_backend_sym_operation(
> +static int cryptodev_backend_operation(
>                    CryptoDevBackend *backend,
> -                 CryptoDevBackendSymOpInfo *op_info,
> +                 CryptoDevBackendOpInfo *op_info,
>                    uint32_t queue_index, Error **errp)
>   {
>       CryptoDevBackendClass *bc =
>                         CRYPTODEV_BACKEND_GET_CLASS(backend);
>   
> -    if (bc->do_sym_op) {
> -        return bc->do_sym_op(backend, op_info, queue_index, errp);
> +    if (bc->do_op) {
> +        return bc->do_op(backend, op_info, queue_index, errp);
>       }
>   
>       return -VIRTIO_CRYPTO_ERR;
> @@ -123,20 +123,18 @@ int cryptodev_backend_crypto_operation(
>                    uint32_t queue_index, Error **errp)
>   {
>       VirtIOCryptoReq *req = opaque;
> +    CryptoDevBackendOpInfo *op_info = &req->op_info;
> +    enum CryptoDevBackendAlgType algtype = req->flags;
>   
> -    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> -        CryptoDevBackendSymOpInfo *op_info;
> -        op_info = req->u.sym_op_info;
> -
> -        return cryptodev_backend_sym_operation(backend,
> -                         op_info, queue_index, errp);
> -    } else {
> +    if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
> +        && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
>           error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
> -                   req->flags);
> -       return -VIRTIO_CRYPTO_NOTSUPP;
> +                   algtype);
> +
> +        return -VIRTIO_CRYPTO_NOTSUPP;
>       }
>   
> -    return -VIRTIO_CRYPTO_ERR;
> +    return cryptodev_backend_operation(backend, op_info, queue_index, errp);
>   }
>   
>   static void
> diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
> index dcd80b904d..a98105d9e9 100644
> --- a/hw/virtio/virtio-crypto.c
> +++ b/hw/virtio/virtio-crypto.c
> @@ -83,7 +83,8 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>                  struct iovec *iov, unsigned int out_num)
>   {
>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> -    CryptoDevBackendSymSessionInfo info;
> +    CryptoDevBackendSessionInfo info;
> +    CryptoDevBackendSymSessionInfo *sym_info;
>       int64_t session_id;
>       int queue_index;
>       uint32_t op_type;
> @@ -92,11 +93,13 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>   
>       memset(&info, 0, sizeof(info));
>       op_type = ldl_le_p(&sess_req->op_type);
> -    info.op_type = op_type;
>       info.op_code = opcode;
>   
> +    sym_info = &info.u.sym_sess_info;
> +    sym_info->op_type = op_type;
> +
>       if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
> -        ret = virtio_crypto_cipher_session_helper(vdev, &info,
> +        ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
>                              &sess_req->u.cipher.para,
>                              &iov, &out_num);
>           if (ret < 0) {
> @@ -105,47 +108,47 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>       } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
>           size_t s;
>           /* cipher part */
> -        ret = virtio_crypto_cipher_session_helper(vdev, &info,
> +        ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
>                              &sess_req->u.chain.para.cipher_param,
>                              &iov, &out_num);
>           if (ret < 0) {
>               goto err;
>           }
>           /* hash part */
> -        info.alg_chain_order = ldl_le_p(
> +        sym_info->alg_chain_order = ldl_le_p(
>                                        &sess_req->u.chain.para.alg_chain_order);
> -        info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
> -        info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
> -        if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
> -            info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
> -            info.auth_key_len = ldl_le_p(
> +        sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
> +        sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
> +        if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
> +            sym_info->hash_alg =
> +                ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
> +            sym_info->auth_key_len = ldl_le_p(
>                                &sess_req->u.chain.para.u.mac_param.auth_key_len);
> -            info.hash_result_len = ldl_le_p(
> +            sym_info->hash_result_len = ldl_le_p(
>                              &sess_req->u.chain.para.u.mac_param.hash_result_len);
> -            if (info.auth_key_len > vcrypto->conf.max_auth_key_len) {
> +            if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
>                   error_report("virtio-crypto length of auth key is too big: %u",
> -                             info.auth_key_len);
> +                             sym_info->auth_key_len);
>                   ret = -VIRTIO_CRYPTO_ERR;
>                   goto err;
>               }
>               /* get auth key */
> -            if (info.auth_key_len > 0) {
> -                DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len);
> -                info.auth_key = g_malloc(info.auth_key_len);
> -                s = iov_to_buf(iov, out_num, 0, info.auth_key,
> -                               info.auth_key_len);
> -                if (unlikely(s != info.auth_key_len)) {
> +            if (sym_info->auth_key_len > 0) {
> +                sym_info->auth_key = g_malloc(sym_info->auth_key_len);
> +                s = iov_to_buf(iov, out_num, 0, sym_info->auth_key,
> +                               sym_info->auth_key_len);
> +                if (unlikely(s != sym_info->auth_key_len)) {
>                       virtio_error(vdev,
>                             "virtio-crypto authenticated key incorrect");
>                       ret = -EFAULT;
>                       goto err;
>                   }
> -                iov_discard_front(&iov, &out_num, info.auth_key_len);
> +                iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
>               }
> -        } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
> -            info.hash_alg = ldl_le_p(
> +        } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
> +            sym_info->hash_alg = ldl_le_p(
>                                &sess_req->u.chain.para.u.hash_param.algo);
> -            info.hash_result_len = ldl_le_p(
> +            sym_info->hash_result_len = ldl_le_p(
>                           &sess_req->u.chain.para.u.hash_param.hash_result_len);
>           } else {
>               /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
> @@ -161,13 +164,10 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>       }
>   
>       queue_index = virtio_crypto_vq2q(queue_id);
> -    session_id = cryptodev_backend_sym_create_session(
> +    session_id = cryptodev_backend_create_session(
>                                        vcrypto->cryptodev,
>                                        &info, queue_index, &local_err);
>       if (session_id >= 0) {
> -        DPRINTF("create session_id=%" PRIu64 " successfully\n",
> -                session_id);
> -
>           ret = session_id;
>       } else {
>           if (local_err) {
> @@ -177,11 +177,78 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
>       }
>   
>   err:
> -    g_free(info.cipher_key);
> -    g_free(info.auth_key);
> +    g_free(sym_info->cipher_key);
> +    g_free(sym_info->auth_key);
>       return ret;
>   }
>   
> +static int64_t
> +virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
> +               struct virtio_crypto_akcipher_create_session_req *sess_req,
> +               uint32_t queue_id, uint32_t opcode,
> +               struct iovec *iov, unsigned int out_num)
> +{
> +    VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> +    CryptoDevBackendSessionInfo info = {0};
> +    CryptoDevBackendAsymSessionInfo *asym_info;
> +    int64_t session_id;
> +    int queue_index;
> +    uint32_t algo, keytype, keylen;
> +    g_autofree uint8_t *key = NULL;
> +    Error *local_err = NULL;
> +
> +    algo = ldl_le_p(&sess_req->para.algo);
> +    keytype = ldl_le_p(&sess_req->para.keytype);
> +    keylen = ldl_le_p(&sess_req->para.keylen);
> +
> +    if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
> +         && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {
> +        error_report("unsupported asym keytype: %d", keytype);
> +        return -VIRTIO_CRYPTO_NOTSUPP;
> +    }
> +
> +    if (keylen) {
> +        key = g_malloc(keylen);
> +        if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
> +            virtio_error(vdev, "virtio-crypto asym key incorrect");
> +            return -EFAULT;
> +        }
> +        iov_discard_front(&iov, &out_num, keylen);
> +    }
> +
> +    info.op_code = opcode;
> +    asym_info = &info.u.asym_sess_info;
> +    asym_info->algo = algo;
> +    asym_info->keytype = keytype;
> +    asym_info->keylen = keylen;
> +    asym_info->key = key;
> +    switch (asym_info->algo) {
> +    case VIRTIO_CRYPTO_AKCIPHER_RSA:
> +        asym_info->u.rsa.padding_algo =
> +            ldl_le_p(&sess_req->para.u.rsa.padding_algo);
> +        asym_info->u.rsa.hash_algo =
> +            ldl_le_p(&sess_req->para.u.rsa.hash_algo);
> +        break;
> +
> +    /* TODO DSA&ECDSA handling */
> +
> +    default:
> +        return -VIRTIO_CRYPTO_ERR;
> +    }
> +
> +    queue_index = virtio_crypto_vq2q(queue_id);
> +    session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
> +                     queue_index, &local_err);
> +    if (session_id < 0) {
> +        if (local_err) {
> +            error_report_err(local_err);
> +        }
> +        return -VIRTIO_CRYPTO_ERR;
> +    }
> +
> +    return session_id;
> +}
> +
>   static uint8_t
>   virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
>            struct virtio_crypto_destroy_session_req *close_sess_req,
> @@ -195,7 +262,7 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
>       session_id = ldq_le_p(&close_sess_req->session_id);
>       DPRINTF("close session, id=%" PRIu64 "\n", session_id);
>   
> -    ret = cryptodev_backend_sym_close_session(
> +    ret = cryptodev_backend_close_session(
>                 vcrypto->cryptodev, session_id, queue_id, &local_err);
>       if (ret == 0) {
>           status = VIRTIO_CRYPTO_OK;
> @@ -260,13 +327,22 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>           opcode = ldl_le_p(&ctrl.header.opcode);
>           queue_id = ldl_le_p(&ctrl.header.queue_id);
>   
> +        memset(&input, 0, sizeof(input));
>           switch (opcode) {
>           case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> -            memset(&input, 0, sizeof(input));
>               session_id = virtio_crypto_create_sym_session(vcrypto,
>                                &ctrl.u.sym_create_session,
>                                queue_id, opcode,
>                                out_iov, out_num);
> +            goto check_session;
> +
> +        case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> +            session_id = virtio_crypto_create_asym_session(vcrypto,
> +                             &ctrl.u.akcipher_create_session,
> +                             queue_id, opcode,
> +                             out_iov, out_num);
> +
> +check_session:
>               /* Serious errors, need to reset virtio crypto device */
>               if (session_id == -EFAULT) {
>                   virtqueue_detach_element(vq, elem, 0);
> @@ -290,10 +366,12 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>               virtqueue_push(vq, elem, sizeof(input));
>               virtio_notify(vdev, vq);
>               break;
> +
>           case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
>           case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
>           case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
>           case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
> +        case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
>               status = virtio_crypto_handle_close_session(vcrypto,
>                      &ctrl.u.destroy_session, queue_id);
>               /* The status only occupy one byte, we can directly use it */
> @@ -311,7 +389,6 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
>           case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
>           default:
>               error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
> -            memset(&input, 0, sizeof(input));
>               stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
>               s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
>               if (unlikely(s != sizeof(input))) {
> @@ -339,28 +416,39 @@ static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq,
>       req->in_num = 0;
>       req->in_len = 0;
>       req->flags = CRYPTODEV_BACKEND_ALG__MAX;
> -    req->u.sym_op_info = NULL;
> +    memset(&req->op_info, 0x00, sizeof(req->op_info));
>   }
>   
>   static void virtio_crypto_free_request(VirtIOCryptoReq *req)
>   {
> -    if (req) {
> -        if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> -            size_t max_len;
> -            CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info;
> -
> -            max_len = op_info->iv_len +
> -                      op_info->aad_len +
> -                      op_info->src_len +
> -                      op_info->dst_len +
> -                      op_info->digest_result_len;
> -
> -            /* Zeroize and free request data structure */
> -            memset(op_info, 0, sizeof(*op_info) + max_len);
> +    if (!req) {
> +        return;
> +    }
> +
> +    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> +        size_t max_len;
> +        CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info;
> +
> +        max_len = op_info->iv_len +
> +                  op_info->aad_len +
> +                  op_info->src_len +
> +                  op_info->dst_len +
> +                  op_info->digest_result_len;
> +
> +        /* Zeroize and free request data structure */
> +        memset(op_info, 0, sizeof(*op_info) + max_len);
> +        g_free(op_info);
> +    } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
> +        CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info;
> +        if (op_info) {
> +            g_free(op_info->src);
> +            g_free(op_info->dst);
> +            memset(op_info, 0, sizeof(*op_info));
>               g_free(op_info);
>           }
> -        g_free(req);
>       }
> +
> +    g_free(req);
>   }
>   
>   static void
> @@ -397,6 +485,35 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev,
>       }
>   }
>   
> +static void
> +virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
> +        VirtIOCryptoReq *req, int32_t status,
> +        CryptoDevBackendAsymOpInfo *asym_op_info)
> +{
> +    size_t s, len;
> +
> +    if (status != VIRTIO_CRYPTO_OK) {
> +        return;
> +    }
> +
> +    len = asym_op_info->dst_len;
> +    if (!len) {
> +        return;
> +    }
> +
> +    s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len);
> +    if (s != len) {
> +        virtio_error(vdev, "virtio-crypto asym dest data incorrect");
> +        return;
> +    }
> +
> +    iov_discard_front(&req->in_iov, &req->in_num, len);
> +
> +    /* For akcipher, dst_len may be changed after operation */
> +    req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
> +}
> +
> +
>   static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>   {
>       VirtIOCrypto *vcrypto = req->vcrypto;
> @@ -404,7 +521,10 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>   
>       if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
>           virtio_crypto_sym_input_data_helper(vdev, req, status,
> -                                            req->u.sym_op_info);
> +                                            req->op_info.u.sym_op_info);
> +    } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
> +        virtio_crypto_akcipher_input_data_helper(vdev, req, status,
> +                                             req->op_info.u.asym_op_info);
>       }
>       stb_p(&req->in->status, status);
>       virtqueue_push(req->vq, &req->elem, req->in_len);
> @@ -543,41 +663,100 @@ err:
>   static int
>   virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto,
>                  struct virtio_crypto_sym_data_req *req,
> -               CryptoDevBackendSymOpInfo **sym_op_info,
> +               CryptoDevBackendOpInfo *op_info,
>                  struct iovec *iov, unsigned int out_num)
>   {
>       VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> +    CryptoDevBackendSymOpInfo *sym_op_info;
>       uint32_t op_type;
> -    CryptoDevBackendSymOpInfo *op_info;
>   
>       op_type = ldl_le_p(&req->op_type);
> -
>       if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
> -        op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para,
> +        sym_op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para,
>                                                 NULL, iov, out_num);
> -        if (!op_info) {
> +        if (!sym_op_info) {
>               return -EFAULT;
>           }
> -        op_info->op_type = op_type;
>       } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
> -        op_info = virtio_crypto_sym_op_helper(vdev, NULL,
> +        sym_op_info = virtio_crypto_sym_op_helper(vdev, NULL,
>                                                 &req->u.chain.para,
>                                                 iov, out_num);
> -        if (!op_info) {
> +        if (!sym_op_info) {
>               return -EFAULT;
>           }
> -        op_info->op_type = op_type;
>       } else {
>           /* VIRTIO_CRYPTO_SYM_OP_NONE */
>           error_report("virtio-crypto unsupported cipher type");
>           return -VIRTIO_CRYPTO_NOTSUPP;
>       }
>   
> -    *sym_op_info = op_info;
> +    sym_op_info->op_type = op_type;
> +    op_info->u.sym_op_info = sym_op_info;
>   
>       return 0;
>   }
>   
> +static int
> +virtio_crypto_handle_asym_req(VirtIOCrypto *vcrypto,
> +               struct virtio_crypto_akcipher_data_req *req,
> +               CryptoDevBackendOpInfo *op_info,
> +               struct iovec *iov, unsigned int out_num)
> +{
> +    VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> +    CryptoDevBackendAsymOpInfo *asym_op_info;
> +    uint32_t src_len;
> +    uint32_t dst_len;
> +    uint32_t len;
> +    uint8_t *src = NULL;
> +    uint8_t *dst = NULL;
> +
> +    asym_op_info = g_malloc0(sizeof(CryptoDevBackendAsymOpInfo));
> +    src_len = ldl_le_p(&req->para.src_data_len);
> +    dst_len = ldl_le_p(&req->para.dst_data_len);
> +
> +    if (src_len > 0) {
> +        src = g_malloc0(src_len);
> +        len = iov_to_buf(iov, out_num, 0, src, src_len);
> +        if (unlikely(len != src_len)) {
> +            virtio_error(vdev, "virtio-crypto asym src data incorrect"
> +                         "expected %u, actual %u", src_len, len);
> +            goto err;
> +        }
> +
> +        iov_discard_front(&iov, &out_num, src_len);
> +    }
> +
> +    if (dst_len > 0) {
> +        dst = g_malloc0(dst_len);
> +
> +        if (op_info->op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
> +            len = iov_to_buf(iov, out_num, 0, dst, dst_len);
> +            if (unlikely(len != dst_len)) {
> +                virtio_error(vdev, "virtio-crypto asym dst data incorrect"
> +                             "expected %u, actual %u", dst_len, len);
> +                goto err;
> +            }
> +
> +            iov_discard_front(&iov, &out_num, dst_len);
> +        }
> +    }
> +
> +    asym_op_info->src_len = src_len;
> +    asym_op_info->dst_len = dst_len;
> +    asym_op_info->src = src;
> +    asym_op_info->dst = dst;
> +    op_info->u.asym_op_info = asym_op_info;
> +
> +    return 0;
> +
> + err:
> +    g_free(asym_op_info);
> +    g_free(src);
> +    g_free(dst);
> +
> +    return -EFAULT;
> +}
> +
>   static int
>   virtio_crypto_handle_request(VirtIOCryptoReq *request)
>   {
> @@ -595,8 +774,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>       unsigned out_num;
>       uint32_t opcode;
>       uint8_t status = VIRTIO_CRYPTO_ERR;
> -    uint64_t session_id;
> -    CryptoDevBackendSymOpInfo *sym_op_info = NULL;
> +    CryptoDevBackendOpInfo *op_info = &request->op_info;
>       Error *local_err = NULL;
>   
>       if (elem->out_num < 1 || elem->in_num < 1) {
> @@ -639,15 +817,28 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>       request->in_iov = in_iov;
>   
>       opcode = ldl_le_p(&req.header.opcode);
> -    session_id = ldq_le_p(&req.header.session_id);
> +    op_info->session_id = ldq_le_p(&req.header.session_id);
> +    op_info->op_code = opcode;
>   
>       switch (opcode) {
>       case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
>       case VIRTIO_CRYPTO_CIPHER_DECRYPT:
> +        op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_SYM;
>           ret = virtio_crypto_handle_sym_req(vcrypto,
> -                         &req.u.sym_req,
> -                         &sym_op_info,
> +                         &req.u.sym_req, op_info,
> +                         out_iov, out_num);
> +        goto check_result;
> +
> +    case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
> +    case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
> +    case VIRTIO_CRYPTO_AKCIPHER_SIGN:
> +    case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
> +        op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_ASYM;
> +        ret = virtio_crypto_handle_asym_req(vcrypto,
> +                         &req.u.akcipher_req, op_info,
>                            out_iov, out_num);
> +
> +check_result:
>           /* Serious errors, need to reset virtio crypto device */
>           if (ret == -EFAULT) {
>               return -1;
> @@ -655,11 +846,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>               virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
>               virtio_crypto_free_request(request);
>           } else {
> -            sym_op_info->session_id = session_id;
>   
>               /* Set request's parameter */
> -            request->flags = CRYPTODEV_BACKEND_ALG_SYM;
> -            request->u.sym_op_info = sym_op_info;
>               ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
>                                       request, queue_index, &local_err);
>               if (ret < 0) {
> @@ -674,6 +862,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
>               virtio_crypto_free_request(request);
>           }
>           break;
> +
>       case VIRTIO_CRYPTO_HASH:
>       case VIRTIO_CRYPTO_MAC:
>       case VIRTIO_CRYPTO_AEAD_ENCRYPT:
> @@ -779,6 +968,7 @@ static void virtio_crypto_init_config(VirtIODevice *vdev)
>       vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l;
>       vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h;
>       vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo;
> +    vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo;
>       vcrypto->conf.max_cipher_key_len =
>                     vcrypto->conf.cryptodev->conf.max_cipher_key_len;
>       vcrypto->conf.max_auth_key_len =
> @@ -891,6 +1081,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config)
>       stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len);
>       stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len);
>       stq_le_p(&crypto_cfg.max_size, c->conf.max_size);
> +    stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo);
>   
>       memcpy(config, &crypto_cfg, c->config_size);
>   }
> diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h
> index a2228d7b2e..348749f5d5 100644
> --- a/include/hw/virtio/virtio-crypto.h
> +++ b/include/hw/virtio/virtio-crypto.h
> @@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf {
>       uint32_t mac_algo_l;
>       uint32_t mac_algo_h;
>       uint32_t aead_algo;
> +    uint32_t akcipher_algo;
>   
>       /* Maximum length of cipher key */
>       uint32_t max_cipher_key_len;
> @@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq {
>       size_t in_len;
>       VirtQueue *vq;
>       struct VirtIOCrypto *vcrypto;
> -    union {
> -        CryptoDevBackendSymOpInfo *sym_op_info;
> -    } u;
> +    CryptoDevBackendOpInfo op_info;
>   } VirtIOCryptoReq;
>   
>   typedef struct VirtIOCryptoQueue {
> diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
> index f4d4057d4d..37c3a360fd 100644
> --- a/include/sysemu/cryptodev.h
> +++ b/include/sysemu/cryptodev.h
> @@ -50,13 +50,13 @@ typedef struct CryptoDevBackendClient
>   
>   enum CryptoDevBackendAlgType {
>       CRYPTODEV_BACKEND_ALG_SYM,
> +    CRYPTODEV_BACKEND_ALG_ASYM,
>       CRYPTODEV_BACKEND_ALG__MAX,
>   };
>   
>   /**
>    * CryptoDevBackendSymSessionInfo:
>    *
> - * @op_code: operation code (refer to virtio_crypto.h)
>    * @cipher_alg: algorithm type of CIPHER
>    * @key_len: byte length of cipher key
>    * @hash_alg: algorithm type of HASH/MAC
> @@ -74,7 +74,6 @@ enum CryptoDevBackendAlgType {
>    */
>   typedef struct CryptoDevBackendSymSessionInfo {
>       /* corresponding with virtio crypto spec */
> -    uint32_t op_code;
>       uint32_t cipher_alg;
>       uint32_t key_len;
>       uint32_t hash_alg;
> @@ -89,11 +88,36 @@ typedef struct CryptoDevBackendSymSessionInfo {
>       uint8_t *auth_key;
>   } CryptoDevBackendSymSessionInfo;
>   
> +/**
> + * CryptoDevBackendAsymSessionInfo:
> + */
> +typedef struct CryptoDevBackendRsaPara {
> +    uint32_t padding_algo;
> +    uint32_t hash_algo;
> +} CryptoDevBackendRsaPara;
> +
> +typedef struct CryptoDevBackendAsymSessionInfo {
> +    /* corresponding with virtio crypto spec */
> +    uint32_t algo;
> +    uint32_t keytype;
> +    uint32_t keylen;
> +    uint8_t *key;
> +    union {
> +        CryptoDevBackendRsaPara rsa;
> +    } u;
> +} CryptoDevBackendAsymSessionInfo;
> +
> +typedef struct CryptoDevBackendSessionInfo {
> +    uint32_t op_code;
> +    union {
> +        CryptoDevBackendSymSessionInfo sym_sess_info;
> +        CryptoDevBackendAsymSessionInfo asym_sess_info;
> +    } u;
> +} CryptoDevBackendSessionInfo;
> +
>   /**
>    * CryptoDevBackendSymOpInfo:
>    *
> - * @session_id: session index which was previously
> - *              created by cryptodev_backend_sym_create_session()
>    * @aad_len: byte length of additional authenticated data
>    * @iv_len: byte length of initialization vector or counter
>    * @src_len: byte length of source data
> @@ -119,7 +143,6 @@ typedef struct CryptoDevBackendSymSessionInfo {
>    *
>    */
>   typedef struct CryptoDevBackendSymOpInfo {
> -    uint64_t session_id;
>       uint32_t aad_len;
>       uint32_t iv_len;
>       uint32_t src_len;
> @@ -138,6 +161,33 @@ typedef struct CryptoDevBackendSymOpInfo {
>       uint8_t data[];
>   } CryptoDevBackendSymOpInfo;
>   
> +
> +/**
> + * CryptoDevBackendAsymOpInfo:
> + *
> + * @src_len: byte length of source data
> + * @dst_len: byte length of destination data
> + * @src: point to the source data
> + * @dst: point to the destination data
> + *
> + */
> +typedef struct CryptoDevBackendAsymOpInfo {
> +    uint32_t src_len;
> +    uint32_t dst_len;
> +    uint8_t *src;
> +    uint8_t *dst;
> +} CryptoDevBackendAsymOpInfo;
> +
> +typedef struct CryptoDevBackendOpInfo {
> +    enum CryptoDevBackendAlgType algtype;
> +    uint32_t op_code;
> +    uint64_t session_id;
> +    union {
> +        CryptoDevBackendSymOpInfo *sym_op_info;
> +        CryptoDevBackendAsymOpInfo *asym_op_info;
> +    } u;
> +} CryptoDevBackendOpInfo;
> +
>   struct CryptoDevBackendClass {
>       ObjectClass parent_class;
>   
> @@ -145,13 +195,13 @@ struct CryptoDevBackendClass {
>       void (*cleanup)(CryptoDevBackend *backend, Error **errp);
>   
>       int64_t (*create_session)(CryptoDevBackend *backend,
> -                       CryptoDevBackendSymSessionInfo *sess_info,
> +                       CryptoDevBackendSessionInfo *sess_info,
>                          uint32_t queue_index, Error **errp);
>       int (*close_session)(CryptoDevBackend *backend,
>                              uint64_t session_id,
>                              uint32_t queue_index, Error **errp);
> -    int (*do_sym_op)(CryptoDevBackend *backend,
> -                     CryptoDevBackendSymOpInfo *op_info,
> +    int (*do_op)(CryptoDevBackend *backend,
> +                     CryptoDevBackendOpInfo *op_info,
>                        uint32_t queue_index, Error **errp);
>   };
>   
> @@ -190,6 +240,7 @@ struct CryptoDevBackendConf {
>       uint32_t mac_algo_l;
>       uint32_t mac_algo_h;
>       uint32_t aead_algo;
> +    uint32_t akcipher_algo;
>       /* Maximum length of cipher key */
>       uint32_t max_cipher_key_len;
>       /* Maximum length of authenticated key */
> @@ -247,34 +298,34 @@ void cryptodev_backend_cleanup(
>              Error **errp);
>   
>   /**
> - * cryptodev_backend_sym_create_session:
> + * cryptodev_backend_create_session:
>    * @backend: the cryptodev backend object
>    * @sess_info: parameters needed by session creating
>    * @queue_index: queue index of cryptodev backend client
>    * @errp: pointer to a NULL-initialized error object
>    *
> - * Create a session for symmetric algorithms
> + * Create a session for symmetric/symmetric algorithms
>    *
>    * Returns: session id on success, or -1 on error
>    */
> -int64_t cryptodev_backend_sym_create_session(
> +int64_t cryptodev_backend_create_session(
>              CryptoDevBackend *backend,
> -           CryptoDevBackendSymSessionInfo *sess_info,
> +           CryptoDevBackendSessionInfo *sess_info,
>              uint32_t queue_index, Error **errp);
>   
>   /**
> - * cryptodev_backend_sym_close_session:
> + * cryptodev_backend_close_session:
>    * @backend: the cryptodev backend object
>    * @session_id: the session id
>    * @queue_index: queue index of cryptodev backend client
>    * @errp: pointer to a NULL-initialized error object
>    *
> - * Close a session for symmetric algorithms which was previously
> - * created by cryptodev_backend_sym_create_session()
> + * Close a session for which was previously
> + * created by cryptodev_backend_create_session()
>    *
>    * Returns: 0 on success, or Negative on error
>    */
> -int cryptodev_backend_sym_close_session(
> +int cryptodev_backend_close_session(
>              CryptoDevBackend *backend,
>              uint64_t session_id,
>              uint32_t queue_index, Error **errp);

-- 
zhenwei pi

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher
  2022-04-28 13:59 ` [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher zhenwei pi
@ 2022-05-12  9:38   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-12  9:38 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:41PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Add unit test and benchmark test for crypto akcipher.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  tests/bench/benchmark-crypto-akcipher.c | 157 ++++++
>  tests/bench/meson.build                 |   4 +
>  tests/bench/test_akcipher_keys.inc      | 537 ++++++++++++++++++
>  tests/unit/meson.build                  |   1 +
>  tests/unit/test-crypto-akcipher.c       | 711 ++++++++++++++++++++++++
>  5 files changed, 1410 insertions(+)
>  create mode 100644 tests/bench/benchmark-crypto-akcipher.c
>  create mode 100644 tests/bench/test_akcipher_keys.inc
>  create mode 100644 tests/unit/test-crypto-akcipher.c


> diff --git a/tests/bench/meson.build b/tests/bench/meson.build
> index 00b3c209dc..f793d972b6 100644
> --- a/tests/bench/meson.build
> +++ b/tests/bench/meson.build
> @@ -23,6 +23,10 @@ if have_block
>    }
>  endif
>  
> +benchs += {
> +    'benchmark-crypto-akcipher': [crypto],
> +}

This needs to moved above a bit to be include the 'if have_block'
section above, otherwise it breaks the build when using --disable-system


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 4/9] crypto: add ASN.1 DER decoder
  2022-04-28 13:59 ` [PATCH v5 4/9] crypto: add ASN.1 DER decoder zhenwei pi
@ 2022-05-12  9:46   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-12  9:46 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:38PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Add an ANS.1 DER decoder which is used to parse asymmetric
> cipher keys
> 
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  crypto/der.c                 | 190 +++++++++++++++++++++++
>  crypto/der.h                 |  82 ++++++++++
>  crypto/meson.build           |   1 +
>  tests/unit/meson.build       |   1 +
>  tests/unit/test-crypto-der.c | 290 +++++++++++++++++++++++++++++++++++
>  5 files changed, 564 insertions(+)
>  create mode 100644 crypto/der.c
>  create mode 100644 crypto/der.h
>  create mode 100644 tests/unit/test-crypto-der.c
> 
> diff --git a/crypto/der.c b/crypto/der.c
> new file mode 100644
> index 0000000000..7907bcfd51
> --- /dev/null
> +++ b/crypto/der.c
> @@ -0,0 +1,190 @@
> +/*
> + * QEMU Crypto ASN.1 DER decoder
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <stdint.h>
> +#include <stddef.h>

These should both be replaced by

  #include "qemu/osdep.h"

otherwise this fails to build for Mingw targets


> +static int qcrypto_der_invoke_callback(DERDecodeCb cb, void *ctx,
> +                                       const uint8_t *value, size_t vlen,
> +                                       Error **errp)
> +{
> +    if (!cb) {
> +        return 0;
> +    }
> +
> +    return cb(ctx, value, vlen, errp);
> +}
> +
> +static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t *dlen,
> +                                             DERDecodeCb cb, void *ctx,
> +                                             Error **errp)
> +{
> +    const uint8_t *value;
> +    size_t vlen = 0;
> +    uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);
> +
> +    /* short format of definite-length */
> +    if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
> +        if (byte_count > *dlen) {
> +            error_setg(errp, "Invalid content length: %u", byte_count);
> +            return -1;
> +        }
> +
> +        value = *data;
> +        vlen = byte_count;
> +        qcrypto_der_cut_nbytes(data, dlen, vlen);
> +
> +        if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
> +            return -1;
> +        }
> +        return vlen;
> +    }
> +
> +    /* Ignore highest bit */
> +    byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;
> +
> +    /*
> +     * size_t is enough to store the value of length, although the DER
> +     * encoding standard supports larger length.
> +     */
> +    if (byte_count > sizeof(size_t)) {
> +        error_setg(errp, "Invalid byte count of content length: %u",
> +                   byte_count);
> +        return -1;
> +    }

> +
> +    if (*dlen < byte_count) {

Can you flip this to   'byte_count > *dlen' so that the ordering
is consistent with the rest of the checks in this method.


> +        error_setg(errp, "Invalid content length: %u", byte_count);
> +        return -1;
> +    }
> +    while (byte_count--) {
> +        vlen <<= 8;
> +        vlen += qcrypto_der_cut_byte(data, dlen);
> +    }
> +
> +    if (vlen > *dlen) {
> +        error_setg(errp, "Invalid content length: %lu", vlen);
> +        return -1;
> +    }
> +
> +    value = *data;
> +    qcrypto_der_cut_nbytes(data, dlen, vlen);
> +
> +    if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
> +        return -1;
> +    }
> +    return vlen;
> +}



> diff --git a/crypto/der.h b/crypto/der.h
> new file mode 100644
> index 0000000000..aaa0e01969
> --- /dev/null
> +++ b/crypto/der.h
> @@ -0,0 +1,82 @@

> +#ifndef QCRYPTO_ASN1_DECODER_H
> +#define QCRYPTO_ASN1_DECODER_H
> +
> +#include "qemu/osdep.h"

osdep.h should always be in the .c file

> +#include "qapi/error.h"
> +
> +/* Simple decoder used to parse DER encoded rsa keys. */
> +
> +/**
> + *  @opaque: user context.
> + *  @value: the starting address of |value| part of 'Tag-Length-Value' pattern.
> + *  @vlen: length of the |value|.
> + *  Returns: 0 for success, any other value is considered an error.
> + */
> +typedef int (*DERDecodeCb) (void *opaque, const uint8_t *value,
> +                            size_t vlen, Error **errp);

Could you call this one   'QCryptoDERDecodeCb)'

> +
> +/**
> + * der_decode_int:

Needs updating for the new func name

> + * @data: pointer to address of input data
> + * @dlen: pointer to length of input data
> + * @cb: callback invoked when decode succeed, if cb equals NULL, no
> + * callback will be invoked
> + * @opaque: parameter passed to cb
> + *
> + * Decode integer from DER-encoded data.
> + *
> + * Returns: On success, *data points to rest data, and *dlen
> + * will be set to the rest length of data, if cb is not NULL, must
> + * return 0 to make decode success, at last, the length of the data
> + * part of the decoded INTEGER will be returned. Otherwise, -1 is
> + * returned.
> + */
> +int qcrypto_der_decode_int(const uint8_t **data,
> +                           size_t *dlen,
> +                           DERDecodeCb cb,
> +                           void *opaque,
> +                           Error **errp);
> +
> +/**
> + * der_decode_seq:

Likewise needs updating

> + *
> + * Decode sequence from DER-encoded data, similar with der_decode_int.
> + *
> + * @data: pointer to address of input data
> + * @dlen: pointer to length of input data
> + * @cb: callback invoked when decode succeed, if cb equals NULL, no
> + * callback will be invoked
> + * @opaque: parameter passed to cb
> + *
> + * Returns: On success, *data points to rest data, and *dlen
> + * will be set to the rest length of data, if cb is not NULL, must
> + * return 0 to make decode success, at last, the length of the data
> + * part of the decoded SEQUENCE will be returned. Otherwise, -1 is
> + * returned.
> + */
> +int qcrypto_der_decode_seq(const uint8_t **data,
> +                           size_t *dlen,
> +                           DERDecodeCb cb,
> +                           void *opaque,
> +                           Error **errp);
> +
> +#endif  /* QCRYPTO_ASN1_DECODER_H */


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 1/9] virtio-crypto: header update
  2022-04-28 13:59 ` [PATCH v5 1/9] virtio-crypto: header update zhenwei pi
@ 2022-05-12  9:55   ` Daniel P. Berrangé
  2022-05-13  3:50     ` zhenwei pi
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-12  9:55 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:35PM +0800, zhenwei pi wrote:
> Update header from linux, support akcipher service.
> 
> Reviewed-by: Gonglei <arei.gonglei@huawei.com>
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> ---
>  .../standard-headers/linux/virtio_crypto.h    | 82 ++++++++++++++++++-
>  1 file changed, 81 insertions(+), 1 deletion(-)

I see these changes were now merged in linux.git with

  commit 24e19590628b58578748eeaec8140bf9c9dc00d9
  Author:     zhenwei pi <pizhenwei@bytedance.com>
  AuthorDate: Wed Mar 2 11:39:15 2022 +0800
  Commit:     Michael S. Tsirkin <mst@redhat.com>
  CommitDate: Mon Mar 28 16:52:58 2022 -0400

    virtio-crypto: introduce akcipher service
    
    Introduce asymmetric service definition, asymmetric operations and
    several well known algorithms.
    
    Co-developed-by: lei he <helei.sig11@bytedance.com>
    Signed-off-by: lei he <helei.sig11@bytedance.com>
    Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
    Link: https://lore.kernel.org/r/20220302033917.1295334-3-pizhenwei@bytedance.com
    Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
    Reviewed-by: Gonglei <arei.gonglei@huawei.com>


And the changes proposed here match that, so

  Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 3/9] crypto: Introduce akcipher crypto class
  2022-04-28 13:59 ` [PATCH v5 3/9] crypto: Introduce akcipher crypto class zhenwei pi
@ 2022-05-12 10:04   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-12 10:04 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:37PM +0800, zhenwei pi wrote:
> Introduce new akcipher crypto class 'QCryptoAkCIpher', which supports
> basic asymmetric operations: encrypt, decrypt, sign and verify.
> 
> Suggested by Daniel P. Berrangé, also add autoptr cleanup for the new
> class. Thanks to Daniel!
> 
> Co-developed-by: lei he <helei.sig11@bytedance.com>
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> ---
>  crypto/akcipher.c         | 102 ++++++++++++++++++++++++
>  crypto/akcipherpriv.h     |  55 +++++++++++++
>  crypto/meson.build        |   1 +
>  include/crypto/akcipher.h | 158 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 316 insertions(+)
>  create mode 100644 crypto/akcipher.c
>  create mode 100644 crypto/akcipherpriv.h
>  create mode 100644 include/crypto/akcipher.h

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Re: [PATCH v5 1/9] virtio-crypto: header update
  2022-05-12  9:55   ` Daniel P. Berrangé
@ 2022-05-13  3:50     ` zhenwei pi
  0 siblings, 0 replies; 23+ messages in thread
From: zhenwei pi @ 2022-05-13  3:50 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

Hi, Daniel

Something I do in my local branch(for the v6 series):
- [PATCH v5 1/9] virtio-crypto: header update
- [PATCH v5 3/9] crypto: Introduce akcipher crypto class
   Add 'Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>'

- [PATCH v5 4/9] crypto: add ASN.1 DER decoder
- [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher
   Fixed the issues you pointed out.

Do you have suggestions about the other patches? Or I'll send the v6 series?

On 5/12/22 17:55, Daniel P. Berrangé wrote:
> On Thu, Apr 28, 2022 at 09:59:35PM +0800, zhenwei pi wrote:
>> Update header from linux, support akcipher service.
>>
>> Reviewed-by: Gonglei <arei.gonglei@huawei.com>
>> Signed-off-by: lei he <helei.sig11@bytedance.com>
>> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
>> ---
>>   .../standard-headers/linux/virtio_crypto.h    | 82 ++++++++++++++++++-
>>   1 file changed, 81 insertions(+), 1 deletion(-)
> 
> I see these changes were now merged in linux.git with
> 
>    commit 24e19590628b58578748eeaec8140bf9c9dc00d9
>    Author:     zhenwei pi <pizhenwei@bytedance.com>
>    AuthorDate: Wed Mar 2 11:39:15 2022 +0800
>    Commit:     Michael S. Tsirkin <mst@redhat.com>
>    CommitDate: Mon Mar 28 16:52:58 2022 -0400
> 
>      virtio-crypto: introduce akcipher service
>      
>      Introduce asymmetric service definition, asymmetric operations and
>      several well known algorithms.
>      
>      Co-developed-by: lei he <helei.sig11@bytedance.com>
>      Signed-off-by: lei he <helei.sig11@bytedance.com>
>      Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
>      Link: https://lore.kernel.org/r/20220302033917.1295334-3-pizhenwei@bytedance.com
>      Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
>      Reviewed-by: Gonglei <arei.gonglei@huawei.com>
> 
> 
> And the changes proposed here match that, so
> 
>    Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> 
> 
> With regards,
> Daniel

-- 
zhenwei pi

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 0/9] Introduce akcipher service for virtio-crypto
  2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (8 preceding siblings ...)
  2022-04-28 13:59 ` [PATCH v5 9/9] crypto: Introduce RSA algorithm zhenwei pi
@ 2022-05-13 10:19 ` Michael S. Tsirkin
  2022-05-13 10:21   ` Michael S. Tsirkin
  9 siblings, 1 reply; 23+ messages in thread
From: Michael S. Tsirkin @ 2022-05-13 10:19 UTC (permalink / raw)
  To: zhenwei pi
  Cc: arei.gonglei, berrange, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:34PM +0800, zhenwei pi wrote:
> Hi, Lei & MST
> 
> Daniel has started to review the akcipher framework and nettle & gcrypt
> implementation, this part seems to be ready soon. Thanks a lot to Daniel!
> 
> And the last patch "crypto: Introduce RSA algorithm" handles akcipher
> requests from guest and uses the new akcipher service. The new feature
> can be used to test by the builtin driver. I would appreciate it if you
> could review patch.


I applied the first 6 patches. Tests need to address Daniel's comments.

> v4 -> v5:
> - Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
> - Rename asn1_decoder.c to der.c.
> - Code style fix: use 'cleanup' & 'error' lables.
> - Allow autoptr type to auto-free.
> - Add test cases for rsakey to handle DER error.
> - Other minor fixes.
> 
> v3 -> v4:
> - Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
> XXX-alg -> XXX-algo.
> - Change version info in qapi/crypto.json, from 7.0 -> 7.1.
> - Remove ecdsa from qapi/crypto.json, it would be introduced with the implemetion later.
> - Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in qapi/crypto.json.
> - Rename arguments of qcrypto_akcipher_XXX to keep aligned with qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add qcrypto_akcipher_max_XXX APIs.
> - Add new API: qcrypto_akcipher_supports.
> - Change the return value of qcrypto_akcipher_enc/dec/sign, these functions return the actual length of result.
> - Separate ASN.1 source code and test case clean.
> - Disable RSA raw encoding for akcipher-nettle.
> - Separate RSA key parser into rsakey.{hc}, and implememts it with builtin-asn1-decoder and nettle respectivly.
> - Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher priority than nettle.
> - For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of returned result maybe less than the dst buffer size, return the actual length of result instead of the buffer length to the guest side. (in function virtio_crypto_akcipher_input_data_helper)
> - Other minor changes.
> 
> Thanks to Daniel!
> 
> Eric pointed out this missing part of use case, send it here again.
> 
> In our plan, the feature is designed for HTTPS offloading case and other applications which use kernel RSA/ecdsa by keyctl syscall. The full picture shows bellow:
> 
> 
>                   Nginx/openssl[1] ... Apps
> Guest   -----------------------------------------
>                    virtio-crypto driver[2]
> -------------------------------------------------
>                    virtio-crypto backend[3]
> Host    -----------------------------------------
>                   /          |          \
>               builtin[4]   vhost     keyctl[5] ...
> 
> 
> [1] User applications can offload RSA calculation to kernel by keyctl syscall. There is no keyctl engine in openssl currently, we developed a engine and tried to contribute it to openssl upstream, but openssl 1.x does not accept new feature. Link:
>     https://github.com/openssl/openssl/pull/16689
> 
> This branch is available and maintained by Lei <helei.sig11@bytedance.com>
>     https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine
> 
> We tested nginx(change config file only) with openssl keyctl engine, it works fine.
> 
> [2] virtio-crypto driver is used to communicate with host side, send requests to host side to do asymmetric calculation.
>     https://lkml.org/lkml/2022/3/1/1425
> 
> [3] virtio-crypto backend handles requests from guest side, and forwards request to crypto backend driver of QEMU.
> 
> [4] Currently RSA is supported only in builtin driver. This driver is supposed to test the full feature without other software(Ex vhost process) and hardware dependence. ecdsa is introduced into qapi type without implementation, this may be implemented in Q3-2022 or later. If ecdsa type definition should be added with the implementation together, I'll remove this in next version.
> 
> [5] keyctl backend is in development, we will post this feature in Q2-2022. keyctl backend can use hardware acceleration(Ex, Intel QAT).
> 
> Setup the full environment, tested with Intel QAT on host side, the QPS of HTTPS increase to ~200% in a guest.
> 
> VS PCI passthrough: the most important benefit of this solution makes the VM migratable.
> 
> v2 -> v3:
> - Introduce akcipher types to qapi
> - Add test/benchmark suite for akcipher class
> - Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
>   - crypto: Introduce akcipher crypto class
>   - virtio-crypto: Introduce RSA algorithm
> 
> v1 -> v2:
> - Update virtio_crypto.h from v2 version of related kernel patch.
> 
> v1:
> - Support akcipher for virtio-crypto.
> - Introduce akcipher class.
> - Introduce ASN1 decoder into QEMU.
> - Implement RSA backend by nettle/hogweed.
> 
> Lei He (6):
>   qapi: crypto-akcipher: Introduce akcipher types to qapi
>   crypto: add ASN.1 DER decoder
>   crypto: Implement RSA algorithm by hogweed
>   crypto: Implement RSA algorithm by gcrypt
>   test/crypto: Add test suite for crypto akcipher
>   tests/crypto: Add test suite for RSA keys
> 
> Zhenwei Pi (3):
>   virtio-crypto: header update
>   crypto: Introduce akcipher crypto class
>   crypto: Introduce RSA algorithm
> 
>  backends/cryptodev-builtin.c                  | 272 ++++-
>  backends/cryptodev-vhost-user.c               |  34 +-
>  backends/cryptodev.c                          |  32 +-
>  crypto/akcipher-gcrypt.c.inc                  | 520 +++++++++
>  crypto/akcipher-nettle.c.inc                  | 432 ++++++++
>  crypto/akcipher.c                             | 108 ++
>  crypto/akcipherpriv.h                         |  55 +
>  crypto/der.c                                  | 190 ++++
>  crypto/der.h                                  |  82 ++
>  crypto/meson.build                            |   6 +
>  crypto/rsakey-builtin.c.inc                   | 209 ++++
>  crypto/rsakey-nettle.c.inc                    | 154 +++
>  crypto/rsakey.c                               |  44 +
>  crypto/rsakey.h                               |  94 ++
>  hw/virtio/virtio-crypto.c                     | 323 ++++--
>  include/crypto/akcipher.h                     | 158 +++
>  include/hw/virtio/virtio-crypto.h             |   5 +-
>  .../standard-headers/linux/virtio_crypto.h    |  82 +-
>  include/sysemu/cryptodev.h                    |  83 +-
>  meson.build                                   |  11 +
>  qapi/crypto.json                              |  64 ++
>  tests/bench/benchmark-crypto-akcipher.c       | 157 +++
>  tests/bench/meson.build                       |   4 +
>  tests/bench/test_akcipher_keys.inc            | 537 ++++++++++
>  tests/unit/meson.build                        |   2 +
>  tests/unit/test-crypto-akcipher.c             | 990 ++++++++++++++++++
>  tests/unit/test-crypto-der.c                  | 290 +++++
>  27 files changed, 4792 insertions(+), 146 deletions(-)
>  create mode 100644 crypto/akcipher-gcrypt.c.inc
>  create mode 100644 crypto/akcipher-nettle.c.inc
>  create mode 100644 crypto/akcipher.c
>  create mode 100644 crypto/akcipherpriv.h
>  create mode 100644 crypto/der.c
>  create mode 100644 crypto/der.h
>  create mode 100644 crypto/rsakey-builtin.c.inc
>  create mode 100644 crypto/rsakey-nettle.c.inc
>  create mode 100644 crypto/rsakey.c
>  create mode 100644 crypto/rsakey.h
>  create mode 100644 include/crypto/akcipher.h
>  create mode 100644 tests/bench/benchmark-crypto-akcipher.c
>  create mode 100644 tests/bench/test_akcipher_keys.inc
>  create mode 100644 tests/unit/test-crypto-akcipher.c
>  create mode 100644 tests/unit/test-crypto-der.c
> 
> -- 
> 2.20.1


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 0/9] Introduce akcipher service for virtio-crypto
  2022-05-13 10:19 ` [PATCH v5 0/9] Introduce akcipher service for virtio-crypto Michael S. Tsirkin
@ 2022-05-13 10:21   ` Michael S. Tsirkin
  0 siblings, 0 replies; 23+ messages in thread
From: Michael S. Tsirkin @ 2022-05-13 10:21 UTC (permalink / raw)
  To: zhenwei pi
  Cc: arei.gonglei, berrange, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Fri, May 13, 2022 at 06:19:10AM -0400, Michael S. Tsirkin wrote:
> On Thu, Apr 28, 2022 at 09:59:34PM +0800, zhenwei pi wrote:
> > Hi, Lei & MST
> > 
> > Daniel has started to review the akcipher framework and nettle & gcrypt
> > implementation, this part seems to be ready soon. Thanks a lot to Daniel!
> > 
> > And the last patch "crypto: Introduce RSA algorithm" handles akcipher
> > requests from guest and uses the new akcipher service. The new feature
> > can be used to test by the builtin driver. I would appreciate it if you
> > could review patch.
> 
> 
> I applied the first 6 patches. Tests need to address Daniel's comments.

Oh sorry, spoke too soon - I noticed mingw issues, and in fact Daniel noticed them too.
Pls address and repost the series. Thanks!

> > v4 -> v5:
> > - Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
> > - Rename asn1_decoder.c to der.c.
> > - Code style fix: use 'cleanup' & 'error' lables.
> > - Allow autoptr type to auto-free.
> > - Add test cases for rsakey to handle DER error.
> > - Other minor fixes.
> > 
> > v3 -> v4:
> > - Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
> > XXX-alg -> XXX-algo.
> > - Change version info in qapi/crypto.json, from 7.0 -> 7.1.
> > - Remove ecdsa from qapi/crypto.json, it would be introduced with the implemetion later.
> > - Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in qapi/crypto.json.
> > - Rename arguments of qcrypto_akcipher_XXX to keep aligned with qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add qcrypto_akcipher_max_XXX APIs.
> > - Add new API: qcrypto_akcipher_supports.
> > - Change the return value of qcrypto_akcipher_enc/dec/sign, these functions return the actual length of result.
> > - Separate ASN.1 source code and test case clean.
> > - Disable RSA raw encoding for akcipher-nettle.
> > - Separate RSA key parser into rsakey.{hc}, and implememts it with builtin-asn1-decoder and nettle respectivly.
> > - Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher priority than nettle.
> > - For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of returned result maybe less than the dst buffer size, return the actual length of result instead of the buffer length to the guest side. (in function virtio_crypto_akcipher_input_data_helper)
> > - Other minor changes.
> > 
> > Thanks to Daniel!
> > 
> > Eric pointed out this missing part of use case, send it here again.
> > 
> > In our plan, the feature is designed for HTTPS offloading case and other applications which use kernel RSA/ecdsa by keyctl syscall. The full picture shows bellow:
> > 
> > 
> >                   Nginx/openssl[1] ... Apps
> > Guest   -----------------------------------------
> >                    virtio-crypto driver[2]
> > -------------------------------------------------
> >                    virtio-crypto backend[3]
> > Host    -----------------------------------------
> >                   /          |          \
> >               builtin[4]   vhost     keyctl[5] ...
> > 
> > 
> > [1] User applications can offload RSA calculation to kernel by keyctl syscall. There is no keyctl engine in openssl currently, we developed a engine and tried to contribute it to openssl upstream, but openssl 1.x does not accept new feature. Link:
> >     https://github.com/openssl/openssl/pull/16689
> > 
> > This branch is available and maintained by Lei <helei.sig11@bytedance.com>
> >     https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine
> > 
> > We tested nginx(change config file only) with openssl keyctl engine, it works fine.
> > 
> > [2] virtio-crypto driver is used to communicate with host side, send requests to host side to do asymmetric calculation.
> >     https://lkml.org/lkml/2022/3/1/1425
> > 
> > [3] virtio-crypto backend handles requests from guest side, and forwards request to crypto backend driver of QEMU.
> > 
> > [4] Currently RSA is supported only in builtin driver. This driver is supposed to test the full feature without other software(Ex vhost process) and hardware dependence. ecdsa is introduced into qapi type without implementation, this may be implemented in Q3-2022 or later. If ecdsa type definition should be added with the implementation together, I'll remove this in next version.
> > 
> > [5] keyctl backend is in development, we will post this feature in Q2-2022. keyctl backend can use hardware acceleration(Ex, Intel QAT).
> > 
> > Setup the full environment, tested with Intel QAT on host side, the QPS of HTTPS increase to ~200% in a guest.
> > 
> > VS PCI passthrough: the most important benefit of this solution makes the VM migratable.
> > 
> > v2 -> v3:
> > - Introduce akcipher types to qapi
> > - Add test/benchmark suite for akcipher class
> > - Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
> >   - crypto: Introduce akcipher crypto class
> >   - virtio-crypto: Introduce RSA algorithm
> > 
> > v1 -> v2:
> > - Update virtio_crypto.h from v2 version of related kernel patch.
> > 
> > v1:
> > - Support akcipher for virtio-crypto.
> > - Introduce akcipher class.
> > - Introduce ASN1 decoder into QEMU.
> > - Implement RSA backend by nettle/hogweed.
> > 
> > Lei He (6):
> >   qapi: crypto-akcipher: Introduce akcipher types to qapi
> >   crypto: add ASN.1 DER decoder
> >   crypto: Implement RSA algorithm by hogweed
> >   crypto: Implement RSA algorithm by gcrypt
> >   test/crypto: Add test suite for crypto akcipher
> >   tests/crypto: Add test suite for RSA keys
> > 
> > Zhenwei Pi (3):
> >   virtio-crypto: header update
> >   crypto: Introduce akcipher crypto class
> >   crypto: Introduce RSA algorithm
> > 
> >  backends/cryptodev-builtin.c                  | 272 ++++-
> >  backends/cryptodev-vhost-user.c               |  34 +-
> >  backends/cryptodev.c                          |  32 +-
> >  crypto/akcipher-gcrypt.c.inc                  | 520 +++++++++
> >  crypto/akcipher-nettle.c.inc                  | 432 ++++++++
> >  crypto/akcipher.c                             | 108 ++
> >  crypto/akcipherpriv.h                         |  55 +
> >  crypto/der.c                                  | 190 ++++
> >  crypto/der.h                                  |  82 ++
> >  crypto/meson.build                            |   6 +
> >  crypto/rsakey-builtin.c.inc                   | 209 ++++
> >  crypto/rsakey-nettle.c.inc                    | 154 +++
> >  crypto/rsakey.c                               |  44 +
> >  crypto/rsakey.h                               |  94 ++
> >  hw/virtio/virtio-crypto.c                     | 323 ++++--
> >  include/crypto/akcipher.h                     | 158 +++
> >  include/hw/virtio/virtio-crypto.h             |   5 +-
> >  .../standard-headers/linux/virtio_crypto.h    |  82 +-
> >  include/sysemu/cryptodev.h                    |  83 +-
> >  meson.build                                   |  11 +
> >  qapi/crypto.json                              |  64 ++
> >  tests/bench/benchmark-crypto-akcipher.c       | 157 +++
> >  tests/bench/meson.build                       |   4 +
> >  tests/bench/test_akcipher_keys.inc            | 537 ++++++++++
> >  tests/unit/meson.build                        |   2 +
> >  tests/unit/test-crypto-akcipher.c             | 990 ++++++++++++++++++
> >  tests/unit/test-crypto-der.c                  | 290 +++++
> >  27 files changed, 4792 insertions(+), 146 deletions(-)
> >  create mode 100644 crypto/akcipher-gcrypt.c.inc
> >  create mode 100644 crypto/akcipher-nettle.c.inc
> >  create mode 100644 crypto/akcipher.c
> >  create mode 100644 crypto/akcipherpriv.h
> >  create mode 100644 crypto/der.c
> >  create mode 100644 crypto/der.h
> >  create mode 100644 crypto/rsakey-builtin.c.inc
> >  create mode 100644 crypto/rsakey-nettle.c.inc
> >  create mode 100644 crypto/rsakey.c
> >  create mode 100644 crypto/rsakey.h
> >  create mode 100644 include/crypto/akcipher.h
> >  create mode 100644 tests/bench/benchmark-crypto-akcipher.c
> >  create mode 100644 tests/bench/test_akcipher_keys.inc
> >  create mode 100644 tests/unit/test-crypto-akcipher.c
> >  create mode 100644 tests/unit/test-crypto-der.c
> > 
> > -- 
> > 2.20.1


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys
  2022-04-28 13:59 ` [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys zhenwei pi
@ 2022-05-13 10:35   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-13 10:35 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:42PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> As Daniel suggested, Add tests suite for rsakey, as a way to prove
> that we can handle DER errors correctly.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  tests/unit/test-crypto-akcipher.c | 285 +++++++++++++++++++++++++++++-
>  1 file changed, 282 insertions(+), 3 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed
  2022-04-28 13:59 ` [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed zhenwei pi
@ 2022-05-13 10:55   ` Daniel P. Berrangé
  2022-05-13 12:26     ` [External] " 何磊
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-13 10:55 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:39PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
> a 'real' RSA backend to handle request from guest side. It's
> important to test RSA offload case without OS & hardware requirement.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> ---
>  crypto/akcipher-nettle.c.inc | 432 +++++++++++++++++++++++++++++++++++
>  crypto/akcipher.c            |   4 +
>  crypto/meson.build           |   4 +
>  crypto/rsakey-builtin.c.inc  | 209 +++++++++++++++++
>  crypto/rsakey-nettle.c.inc   | 154 +++++++++++++
>  crypto/rsakey.c              |  44 ++++
>  crypto/rsakey.h              |  94 ++++++++
>  meson.build                  |  11 +
>  8 files changed, 952 insertions(+)
>  create mode 100644 crypto/akcipher-nettle.c.inc
>  create mode 100644 crypto/rsakey-builtin.c.inc
>  create mode 100644 crypto/rsakey-nettle.c.inc
>  create mode 100644 crypto/rsakey.c
>  create mode 100644 crypto/rsakey.h
> 


> +
> +static void wrap_nettle_random_func(void *ctx, size_t len, uint8_t *out)
> +{
> +    /* TODO: check result */
> +    qcrypto_random_bytes(out, len, NULL);
> +}

Unfortunate meson requires this function to be void.

Since we've no way to report errors, then our only option is assume
qcrypto_random_bytes will never fail, and enforce that assumptiomn
by passing '&error_abort' for the last parameter.

> +
> +static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher,
> +                                      const void *data, size_t data_len,
> +                                      void *enc, size_t enc_len,
> +                                      Error **errp)
> +{
> +
> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
> +    mpz_t c;
> +    int ret = -1;
> +
> +    if (data_len > rsa->pub.size || enc_len != rsa->pub.size) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }

Can you report the invalid & expect buffer sizes, as it'll
make debugging much easier. You'll need a separate check
and error reporting for enc_len and data_len.

> +
> +    /* Nettle do not support RSA encryption without any padding */
> +    switch (rsa->padding_alg) {
> +    case QCRYPTO_RSA_PADDING_ALG_RAW:
> +        error_setg(errp, "RSA with raw padding is not supported");
> +        break;
> +
> +    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
> +        mpz_init(c);
> +        if (rsa_encrypt(&rsa->pub, NULL, wrap_nettle_random_func,
> +                          data_len, (uint8_t *)data, c) != 1) {
> +            error_setg(errp, "Failed to encrypt");
> +        } else {
> +            nettle_mpz_get_str_256(enc_len, (uint8_t *)enc, c);
> +            ret = enc_len;
> +        }
> +        mpz_clear(c);
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown padding");
> +    }
> +
> +    return ret;
> +}
> +
> +static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,
> +                                      const void *enc, size_t enc_len,
> +                                      void *data, size_t data_len,
> +                                      Error **errp)
> +{
> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
> +    mpz_t c;
> +    int ret = -1;
> +    if (enc_len > rsa->priv.size) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }

Again please report the invalid & expected sizes in the message

We don't need to validate 'data_len' in the decrypt case,
as you did in encrypt ?

> +
> +    switch (rsa->padding_alg) {
> +    case QCRYPTO_RSA_PADDING_ALG_RAW:
> +        error_setg(errp, "RSA with raw padding is not supported");
> +        break;
> +
> +    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
> +        nettle_mpz_init_set_str_256_u(c, enc_len, enc);
> +        if (!rsa_decrypt(&rsa->priv, &data_len, (uint8_t *)data, c)) {
> +            error_setg(errp, "Failed to decrypt");
> +        } else {
> +            ret = data_len;
> +        }
> +
> +        mpz_clear(c);
> +        break;
> +
> +    default:
> +        ret = -1;

'ret' was initialized to '-1' at time of declaration

> +        error_setg(errp, "Unknown padding");
> +    }
> +
> +    return ret;
> +}
> +
> +static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher,
> +                                   const void *data, size_t data_len,
> +                                   void *sig, size_t sig_len, Error **errp)
> +{
> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
> +    int ret;

For consistency with the earlier methods, initialize this to -1

> +    mpz_t s;
> +
> +    /**
> +     * The RSA algorithm cannot be used for signature/verification
> +     * without padding.
> +     */
> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
> +        error_setg(errp, "Try to make signature without padding");
> +        return -1;
> +    }
> +
> +    if (data_len > rsa->priv.size || sig_len != rsa->priv.size) {
> +        error_setg(errp, "Invalid buffer size");
> +        return -1;
> +    }

Same note about reporting the lengths.

> +
> +    mpz_init(s);
> +    switch (rsa->hash_alg) {
> +    case QCRYPTO_HASH_ALG_MD5:
> +        ret = rsa_md5_sign_digest(&rsa->priv, data, s);

I'd suggest using a separate variable 'rv' here, as I
find it can be confusing to re-use a variable for two
different purposes. Keep 'ret' exclusively for holdnig
the method return value.

> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA1:
> +        ret = rsa_sha1_sign_digest(&rsa->priv, data, s);
> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA256:
> +        ret = rsa_sha256_sign_digest(&rsa->priv, data, s);
> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA512:
> +        ret = rsa_sha512_sign_digest(&rsa->priv, data, s);
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown hash algorithm");
> +        ret = -1;

No need if we initialize 'ret' upfront.

> +        goto cleanup;
> +    }
> +
> +    if (ret != 1) {
> +        error_setg(errp, "Failed to make signature");
> +        ret = -1;
> +        goto cleanup;
> +    }
> +    nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
> +    ret = sig_len;
> +
> +cleanup:
> +    mpz_clear(s);
> +
> +    return ret;
> +}
> +
> +static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher,
> +                                     const void *sig, size_t sig_len,
> +                                     const void *data, size_t data_len,
> +                                     Error **errp)
> +{
> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
> +
> +    int ret;

Initialize to -1 here.

> +    mpz_t s;
> +
> +    /**
> +     * The RSA algorithm cannot be used for signature/verification
> +     * without padding.
> +     */
> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
> +        error_setg(errp, "Operation not supported");
> +        return -1;
> +    }
> +    if (data_len > rsa->pub.size || sig_len < rsa->pub.size) {
> +        error_setg(errp, "Invalid buffer size");
> +        return -1;
> +    }

Ssame note as earlier methods

> +
> +    nettle_mpz_init_set_str_256_u(s, sig_len, sig);
> +    switch (rsa->hash_alg) {
> +    case QCRYPTO_HASH_ALG_MD5:
> +        ret = rsa_md5_verify_digest(&rsa->pub, data, s);
> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA1:
> +        ret = rsa_sha1_verify_digest(&rsa->pub, data, s);
> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA256:
> +        ret = rsa_sha256_verify_digest(&rsa->pub, data, s);
> +        break;
> +
> +    case QCRYPTO_HASH_ALG_SHA512:
> +        ret = rsa_sha512_verify_digest(&rsa->pub, data, s);
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unsupported hash algorithm");
> +        ret = -1;

Skip this

> +        goto cleanup;
> +    }
> +
> +    if (ret != 1) {
> +        error_setg(errp, "Failed to verify");
> +        ret = -1;
> +        goto cleanup;
> +    }
> +    ret = 0;
> +
> +cleanup:
> +    mpz_clear(s);
> +
> +    return ret;
> +}
> +
> +QCryptoAkCipherDriver nettle_rsa = {
> +    .encrypt = qcrypto_nettle_rsa_encrypt,
> +    .decrypt = qcrypto_nettle_rsa_decrypt,
> +    .sign = qcrypto_nettle_rsa_sign,
> +    .verify = qcrypto_nettle_rsa_verify,
> +    .free = qcrypto_nettle_rsa_free,
> +};
> +
> +static QCryptoAkCipher *qcrypto_nettle_rsa_new(
> +    const QCryptoAkCipherOptionsRSA *opt,
> +    QCryptoAkCipherKeyType type,
> +    const uint8_t *key, size_t keylen,
> +    Error **errp)
> +{
> +    QCryptoNettleRSA *rsa = g_new0(QCryptoNettleRSA, 1);
> +
> +    rsa->padding_alg = opt->padding_alg;
> +    rsa->hash_alg = opt->hash_alg;
> +    rsa->akcipher.driver = &nettle_rsa;
> +    rsa_public_key_init(&rsa->pub);
> +    rsa_private_key_init(&rsa->priv);
> +
> +    switch (type) {
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> +        if (qcrypt_nettle_parse_rsa_private_key(rsa, key, keylen) != 0) {
> +            error_setg(errp, "Failed to parse rsa private key");
> +            goto error;
> +        }
> +        break;
> +
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> +        if (qcrypt_nettle_parse_rsa_public_key(rsa, key, keylen) != 0) {
> +            error_setg(errp, "Failed to parse rsa public rsa key");
> +            goto error;
> +        }
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown akcipher key type %d", type);
> +        goto error;
> +    }
> +
> +    return (QCryptoAkCipher *)rsa;
> +
> +error:
> +    qcrypto_nettle_rsa_free((QCryptoAkCipher *)rsa);
> +    return NULL;
> +}
> +
> +
> +bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
> +{
> +    switch (opts->alg) {
> +    case QCRYPTO_AKCIPHER_ALG_RSA:
> +        switch (opts->u.rsa.padding_alg) {
> +        case QCRYPTO_RSA_PADDING_ALG_PKCS1:
> +            switch (opts->u.rsa.hash_alg) {
> +            case QCRYPTO_HASH_ALG_MD5:
> +            case QCRYPTO_HASH_ALG_SHA1:
> +            case QCRYPTO_HASH_ALG_SHA256:
> +            case QCRYPTO_HASH_ALG_SHA512:
> +                return true;
> +
> +            default:
> +                return false;
> +            }
> +
> +        case QCRYPTO_RSA_PADDING_ALG_RAW:
> +        default:
> +            return false;
> +        }
> +        break;
> +
> +    default:
> +        return false;
> +    }
> +}
> diff --git a/crypto/akcipher.c b/crypto/akcipher.c
> index ab28bf415b..f287083f92 100644
> --- a/crypto/akcipher.c
> +++ b/crypto/akcipher.c
> @@ -23,6 +23,9 @@
>  #include "crypto/akcipher.h"
>  #include "akcipherpriv.h"
>  
> +#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
> +#include "akcipher-nettle.c.inc"
> +#else
>  QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
>                                        QCryptoAkCipherKeyType type,
>                                        const uint8_t *key, size_t keylen,
> @@ -37,6 +40,7 @@ bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
>  {
>      return false;
>  }
> +#endif
>  
>  int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
>                               const void *in, size_t in_len,
> diff --git a/crypto/meson.build b/crypto/meson.build
> index c9b36857a6..d9294eed83 100644
> --- a/crypto/meson.build
> +++ b/crypto/meson.build
> @@ -21,10 +21,14 @@ crypto_ss.add(files(
>    'tlscredspsk.c',
>    'tlscredsx509.c',
>    'tlssession.c',
> +  'rsakey.c',
>  ))
>  
>  if nettle.found()
>    crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c'))
> +  if hogweed.found()
> +    crypto_ss.add(gmp, hogweed)
> +  endif
>    if xts == 'private'
>      crypto_ss.add(files('xts.c'))
>    endif
> diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc
> new file mode 100644
> index 0000000000..0a93712f4f
> --- /dev/null
> +++ b/crypto/rsakey-builtin.c.inc
> @@ -0,0 +1,209 @@
> +/*
> + * QEMU Crypto akcipher algorithms
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "der.h"
> +#include "rsakey.h"
> +
> +static int extract_mpi(void *ctx, const uint8_t *value,
> +                       size_t vlen, Error **errp)
> +{
> +    QCryptoAkCipherMPI *mpi = (QCryptoAkCipherMPI *)ctx;
> +    if (vlen == 0) {
> +        error_setg(errp, "Empty mpi field");
> +        return -1;
> +    }
> +    mpi->data = g_memdup2(value, vlen);
> +    mpi->len = vlen;
> +    return 0;
> +}
> +
> +static int extract_version(void *ctx, const uint8_t *value,
> +                           size_t vlen, Error **errp)
> +{
> +    uint8_t *version = (uint8_t *)ctx;
> +    if (vlen != 1 || *value > 1) {
> +        error_setg(errp, "Invalid rsakey version");
> +        return -1;
> +    }
> +    *version = *value;
> +    return 0;
> +}
> +
> +static int extract_seq_content(void *ctx, const uint8_t *value,
> +                               size_t vlen, Error **errp)
> +{
> +    const uint8_t **content = (const uint8_t **)ctx;
> +    if (vlen == 0) {
> +        error_setg(errp, "Empty sequence");
> +        return -1;
> +    }
> +    *content = value;
> +    return 0;
> +}
> +
> +/**
> + *
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + */
> +static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse(
> +    const uint8_t *key, size_t keylen)
> +{
> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
> +    const uint8_t *seq;
> +    size_t seq_length;
> +    int decode_ret;
> +    Error *local_error = NULL;
> +
> +    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
> +        extract_seq_content, &seq, &local_error);
> +    if (decode_ret < 0) {
> +        error_report_err(local_error);

Nothing in the crypto/ directory should ever call error_report_err.
Any methods  which can fail need to have an 'Error **errp' parameter
and propagate this back to the caller(s).

> +        goto error;
> +    }
> +    if (keylen != 0) {
> +        goto error;
> +    }
> +    seq_length = decode_ret;
> +
> +    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
> +                               &rsa->n, &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
> +                               &rsa->e, &local_error) < 0) {
> +        error_report_err(local_error);
> +        goto error;
> +    }
> +    if (seq_length != 0) {
> +        goto error;
> +    }
> +
> +    return rsa;
> +
> +error:
> +    qcrypto_akcipher_rsakey_free(rsa);
> +    return NULL;
> +}
> +
> +/**
> + *        RsaPrivKey ::= SEQUENCE {
> + *             version     INTEGER
> + *             n           INTEGER
> + *             e           INTEGER
> + *             d           INTEGER
> + *             p           INTEGER
> + *             q           INTEGER
> + *             dp          INTEGER
> + *             dq          INTEGER
> + *             u           INTEGER
> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
> + *         }
> + */
> +static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse(
> +    const uint8_t *key, size_t keylen)
> +{
> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
> +    uint8_t version;
> +    const uint8_t *seq;
> +    int decode_ret;
> +    size_t seq_length;
> +    Error *local_error = NULL;
> +
> +    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
> +        extract_seq_content, &seq, &local_error);
> +    if (decode_ret < 0) {
> +        error_report_err(local_error);
> +        goto error;
> +    }
> +    if (keylen != 0) {
> +        goto error;
> +    }
> +    seq_length = decode_ret;
> +
> +    decode_ret = qcrypto_der_decode_int(&seq, &seq_length, extract_version,
> +                                        &version, &local_error);
> +    if (decode_ret < 0) {
> +        error_report_err(local_error);
> +        goto error;
> +    }
> +
> +    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
> +                               &rsa->n, &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
> +                               &rsa->e, &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
> +                               &rsa->d, &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p,
> +                               &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q,
> +                               &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp,
> +                               &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq,
> +                               &local_error) < 0 ||
> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u,
> +                               &local_error) < 0) {
> +        error_report_err(local_error);
> +        goto error;
> +    }
> +    /**
> +     * According to the standard, otherPrimeInfos must be present for version 1.
> +     * There is no strict verification here, this is to be compatible with
> +     * the unit test of the kernel. TODO: remove this until linux kernel's
> +     * unit-test is fixed.
> +     */
> +    if (version == 1 && seq_length != 0) {
> +        if (qcrypto_der_decode_seq(&seq, &seq_length,
> +                                   NULL, NULL, &local_error) < 0) {
> +            error_report_err(local_error);
> +            goto error;
> +        }
> +        if (seq_length != 0) {
> +            goto error;
> +        }
> +        return rsa;
> +    }
> +    if (seq_length != 0) {
> +        goto error;
> +    }
> +
> +    return rsa;
> +
> +error:
> +    qcrypto_akcipher_rsakey_free(rsa);
> +    return NULL;
> +}
> +
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
> +    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
> +{
> +    switch (type) {
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> +        return qcrypto_builtin_rsa_private_key_parse(key, keylen);
> +
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> +        return qcrypto_builtin_rsa_public_key_parse(key, keylen);
> +
> +    default:
> +        return NULL;
> +    }
> +}
> diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
> new file mode 100644
> index 0000000000..2c89b3be88
> --- /dev/null
> +++ b/crypto/rsakey-nettle.c.inc
> @@ -0,0 +1,154 @@
> +/*
> + * QEMU Crypto akcipher algorithms
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <nettle/asn1.h>
> +#include <stdbool.h>
> +
> +#include "rsakey.h"
> +
> +static bool DumpMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
> +{
> +    mpi->data = g_memdup2(i->data, i->length);
> +    mpi->len = i->length;
> +    return true;
> +}
> +
> +static bool GetMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
> +{
> +    if (asn1_der_iterator_next(i) != ASN1_ITERATOR_PRIMITIVE ||
> +        i->type != ASN1_INTEGER) {
> +        return false;
> +    }
> +    return DumpMPI(i, mpi);
> +}
> +
> +
> +/**
> + *        RsaPrivKey ::= SEQUENCE {
> + *             version     INTEGER
> + *             n           INTEGER
> + *             e           INTEGER
> + *             d           INTEGER
> + *             p           INTEGER
> + *             q           INTEGER
> + *             dp          INTEGER
> + *             dq          INTEGER
> + *             u           INTEGER
> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
> + *         }
> + */
> +static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_private_key_parse(
> +    const uint8_t *key, size_t keylen)
> +{
> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
> +    struct asn1_der_iterator i;
> +    uint32_t version;
> +    int tag;
> +
> +    /* Parse entire struct */
> +    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
> +        i.type != ASN1_SEQUENCE ||
> +        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
> +        i.type != ASN1_INTEGER ||
> +        !asn1_der_get_uint32(&i, &version) ||
> +        version > 1 ||
> +        !GetMPI(&i, &rsa->n) ||
> +        !GetMPI(&i, &rsa->e) ||
> +        !GetMPI(&i, &rsa->d) ||
> +        !GetMPI(&i, &rsa->p) ||
> +        !GetMPI(&i, &rsa->q) ||
> +        !GetMPI(&i, &rsa->dp) ||
> +        !GetMPI(&i, &rsa->dq) ||
> +        !GetMPI(&i, &rsa->u)) {
> +        goto error;
> +    }
> +
> +    if (version == 1) {
> +        tag = asn1_der_iterator_next(&i);
> +        /**
> +         * According to the standard otherPrimeInfos must be present for
> +         * version 1. There is no strict verification here, this is to be
> +         * compatible with the unit test of the kernel. TODO: remove this
> +         * until linux-kernel's unit-test is fixed;
> +         */
> +        if (tag == ASN1_ITERATOR_END) {
> +            return rsa;
> +        }
> +        if (tag != ASN1_ITERATOR_CONSTRUCTED ||
> +            i.type != ASN1_SEQUENCE) {
> +            goto error;
> +        }
> +    }
> +
> +    if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
> +        goto error;
> +    }
> +
> +    return rsa;
> +
> +error:
> +    qcrypto_akcipher_rsakey_free(rsa);
> +    return NULL;
> +}
> +
> +/**
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + */
> +static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_public_key_parse(
> +    const uint8_t *key, size_t keylen)
> +{
> +
> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
> +    struct asn1_der_iterator i;
> +
> +    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
> +        i.type != ASN1_SEQUENCE ||
> +        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
> +        !DumpMPI(&i, &rsa->n) ||
> +        !GetMPI(&i, &rsa->e) ||
> +        asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
> +        goto error;
> +    }
> +
> +    return rsa;
> +
> +error:
> +    qcrypto_akcipher_rsakey_free(rsa);
> +    return NULL;
> +}
> +
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
> +    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
> +{
> +    switch (type) {
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> +        return qcrypto_nettle_rsa_private_key_parse(key, keylen);
> +
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> +        return qcrypto_nettle_rsa_public_key_parse(key, keylen);
> +
> +    default:
> +        return NULL;
> +    }
> +}
> diff --git a/crypto/rsakey.c b/crypto/rsakey.c
> new file mode 100644
> index 0000000000..cc40e072f0
> --- /dev/null
> +++ b/crypto/rsakey.c
> @@ -0,0 +1,44 @@
> +/*
> + * QEMU Crypto RSA key parser
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "rsakey.h"
> +
> +void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
> +{
> +    if (!rsa_key) {
> +        return;
> +    }
> +    g_free(rsa_key->n.data);
> +    g_free(rsa_key->e.data);
> +    g_free(rsa_key->d.data);
> +    g_free(rsa_key->p.data);
> +    g_free(rsa_key->q.data);
> +    g_free(rsa_key->dp.data);
> +    g_free(rsa_key->dq.data);
> +    g_free(rsa_key->u.data);
> +    g_free(rsa_key);
> +}
> +
> +#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
> +#include "rsakey-nettle.c.inc"
> +#else
> +#include "rsakey-builtin.c.inc"
> +#endif
> diff --git a/crypto/rsakey.h b/crypto/rsakey.h
> new file mode 100644
> index 0000000000..a1e04ae021
> --- /dev/null
> +++ b/crypto/rsakey.h
> @@ -0,0 +1,94 @@
> +/*
> + * QEMU Crypto RSA key parser
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef QCRYPTO_RSAKEY_H
> +#define QCRYPTO_RSAKEY_H
> +
> +#include <nettle/bignum.h>
> +
> +#include "qemu/osdep.h"
> +#include "qemu/host-utils.h"
> +#include "crypto/akcipher.h"
> +
> +typedef struct QCryptoAkCipherRSAKey QCryptoAkCipherRSAKey;
> +typedef struct QCryptoAkCipherMPI QCryptoAkCipherMPI;
> +
> +/**
> + * Multiple precious integer, encoded as two' complement,
> + * copied directly from DER encoded ASN.1 structures.
> + */
> +struct QCryptoAkCipherMPI {
> +    uint8_t *data;
> +    size_t len;
> +};
> +
> +/* See rfc2437: https://datatracker.ietf.org/doc/html/rfc2437 */
> +struct QCryptoAkCipherRSAKey {
> +    /* The modulus */
> +    QCryptoAkCipherMPI n;
> +    /* The public exponent */
> +    QCryptoAkCipherMPI e;
> +    /* The private exponent */
> +    QCryptoAkCipherMPI d;
> +    /* The first factor */
> +    QCryptoAkCipherMPI p;
> +    /* The second factor */
> +    QCryptoAkCipherMPI q;
> +    /* The first factor's exponent */
> +    QCryptoAkCipherMPI dp;
> +    /* The second factor's exponent */
> +    QCryptoAkCipherMPI dq;
> +    /* The CRT coefficient */
> +    QCryptoAkCipherMPI u;
> +};
> +
> +/**
> + * Parse DER encoded ASN.1 RSA keys, expected ASN.1 schemas:
> + *        RsaPrivKey ::= SEQUENCE {
> + *             version     INTEGER
> + *             n           INTEGER
> + *             e           INTEGER
> + *             d           INTEGER
> + *             p           INTEGER
> + *             q           INTEGER
> + *             dp          INTEGER
> + *             dq          INTEGER
> + *             u           INTEGER
> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
> + *         }
> + *
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + *
> + * Returns: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
> +    QCryptoAkCipherKeyType type,
> +    const uint8_t *key, size_t keylen);
> +
> +void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
> +
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
> +                              qcrypto_akcipher_rsakey_free);
> +
> +#endif
> diff --git a/meson.build b/meson.build
> index d083c6b7bf..fd0bf7aa5d 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -1049,6 +1049,7 @@ endif
>  # gcrypt over nettle for performance reasons.
>  gcrypt = not_found
>  nettle = not_found
> +hogweed = not_found
>  xts = 'none'
>  
>  if get_option('nettle').enabled() and get_option('gcrypt').enabled()
> @@ -1086,6 +1087,15 @@ if not gnutls_crypto.found()
>    endif
>  endif
>  
> +gmp = dependency('gmp', required: false, method: 'pkg-config', kwargs: static_kwargs)
> +if nettle.found() and gmp.found()
> +  hogweed = dependency('hogweed', version: '>=3.4',
> +                       method: 'pkg-config',
> +                       required: get_option('nettle'),
> +                       kwargs: static_kwargs)
> +endif
> +
> +
>  gtk = not_found
>  gtkx11 = not_found
>  vte = not_found
> @@ -1567,6 +1577,7 @@ config_host_data.set('CONFIG_GNUTLS', gnutls.found())
>  config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found())
>  config_host_data.set('CONFIG_GCRYPT', gcrypt.found())
>  config_host_data.set('CONFIG_NETTLE', nettle.found())
> +config_host_data.set('CONFIG_HOGWEED', hogweed.found())
>  config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
>  config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
>  config_host_data.set('CONFIG_STATX', has_statx)
> -- 
> 2.20.1
> 

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt
  2022-04-28 13:59 ` [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt zhenwei pi
@ 2022-05-13 11:00   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-13 11:00 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, jasowang, cohuck

On Thu, Apr 28, 2022 at 09:59:40PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Added gcryt implementation of RSA algorithm, RSA algorithm
> implemented by gcrypt has a higher priority than nettle because
> it supports raw padding.
> 
> Signed-off-by: lei he <helei.sig11@bytedance.com>
> ---
>  crypto/akcipher-gcrypt.c.inc | 520 +++++++++++++++++++++++++++++++++++
>  crypto/akcipher.c            |   4 +-
>  2 files changed, 523 insertions(+), 1 deletion(-)
>  create mode 100644 crypto/akcipher-gcrypt.c.inc
> 
> diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc
> new file mode 100644
> index 0000000000..32ff502f71
> --- /dev/null
> +++ b/crypto/akcipher-gcrypt.c.inc
> @@ -0,0 +1,520 @@
> +/*
> + * QEMU Crypto akcipher algorithms
> + *
> + * Copyright (c) 2022 Bytedance
> + * Author: lei he <helei.sig11@bytedance.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <gcrypt.h>
> +
> +#include "qemu/osdep.h"
> +#include "qemu/host-utils.h"
> +#include "crypto/akcipher.h"
> +#include "crypto/random.h"
> +#include "qapi/error.h"
> +#include "sysemu/cryptodev.h"
> +#include "rsakey.h"
> +
> +typedef struct QCryptoGcryptRSA {
> +    QCryptoAkCipher akcipher;
> +    gcry_sexp_t key;
> +    QCryptoRSAPaddingAlgorithm padding_alg;
> +    QCryptoHashAlgorithm hash_alg;
> +} QCryptoGcryptRSA;
> +
> +static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher)
> +{
> +    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
> +    if (!rsa) {
> +        return;
> +    }
> +
> +    gcry_sexp_release(rsa->key);
> +    g_free(rsa);
> +}
> +
> +static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
> +    const QCryptoAkCipherOptionsRSA *opt,
> +    QCryptoAkCipherKeyType type,
> +    const uint8_t *key,  size_t keylen,
> +    Error **errp);
> +
> +QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
> +                                      QCryptoAkCipherKeyType type,
> +                                      const uint8_t *key, size_t keylen,
> +                                      Error **errp)
> +{
> +    switch (opts->alg) {
> +    case QCRYPTO_AKCIPHER_ALG_RSA:
> +        return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new(
> +            &opts->u.rsa, type, key, keylen, errp);
> +
> +    default:
> +        error_setg(errp, "Unsupported algorithm: %u", opts->alg);
> +        return NULL;
> +    }
> +
> +    return NULL;
> +}
> +
> +static void qcrypto_gcrypt_set_rsa_size(QCryptoAkCipher *akcipher, gcry_mpi_t n)
> +{
> +    size_t key_size = (gcry_mpi_get_nbits(n) + 7) / 8;
> +    akcipher->max_plaintext_len = key_size;
> +    akcipher->max_ciphertext_len = key_size;
> +    akcipher->max_dgst_len = key_size;
> +    akcipher->max_signature_len = key_size;
> +}
> +
> +static int qcrypto_gcrypt_parse_rsa_private_key(
> +    QCryptoGcryptRSA *rsa,
> +    const uint8_t *key, size_t keylen)
> +{
> +    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
> +        QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen);
> +    gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
> +    bool compute_mul_inv = false;
> +    int ret = -1;
> +    gcry_error_t err;
> +
> +    if (!rsa_key) {
> +        return ret;
> +    }

If qcrypto_akcipher_rsakey_parse can fail, we need to get a
'Error **errp' in/out of it

> +
> +    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
> +                        rsa_key->n.data, rsa_key->n.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

Please add an 'Error **errp' parameter to this method, and
populate it with an error message that includes the output
of gcry_

> +
> +    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
> +                        rsa_key->e.data, rsa_key->e.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    err = gcry_mpi_scan(&d, GCRYMPI_FMT_STD,
> +                        rsa_key->d.data, rsa_key->d.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    err = gcry_mpi_scan(&p, GCRYMPI_FMT_STD,
> +                        rsa_key->p.data, rsa_key->p.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    err = gcry_mpi_scan(&q, GCRYMPI_FMT_STD,
> +                        rsa_key->q.data, rsa_key->q.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    if (gcry_mpi_cmp_ui(p, 0) > 0 && gcry_mpi_cmp_ui(q, 0) > 0) {
> +        compute_mul_inv = true;
> +
> +        u = gcry_mpi_new(0);
> +        if (gcry_mpi_cmp(p, q) > 0) {
> +            gcry_mpi_swap(p, q);
> +        }
> +        gcry_mpi_invm(u, p, q);
> +    }
> +
> +    if (compute_mul_inv) {
> +        err = gcry_sexp_build(&rsa->key, NULL,
> +            "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))",
> +            n, e, d, p, q, u);
> +    } else {
> +        err = gcry_sexp_build(&rsa->key, NULL,
> +            "(private-key (rsa (n %m) (e %m) (d %m)))", n, e, d);
> +    }
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa,  n);
> +    ret = 0;
> +
> +cleanup:
> +    gcry_mpi_release(n);
> +    gcry_mpi_release(e);
> +    gcry_mpi_release(d);
> +    gcry_mpi_release(p);
> +    gcry_mpi_release(q);
> +    gcry_mpi_release(u);
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_parse_rsa_public_key(QCryptoGcryptRSA *rsa,
> +                                               const uint8_t *key,
> +                                               size_t keylen)
> +{
> +
> +    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
> +        QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen);
> +    gcry_mpi_t n = NULL, e = NULL;
> +    int ret = -1;
> +    gcry_error_t err;
> +
> +    if (!rsa_key) {
> +        return ret;
> +    }
> +
> +    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
> +                        rsa_key->n.data, rsa_key->n.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

Same note about having 'Error **errp' parameters for this
method.

> +
> +    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
> +                        rsa_key->e.data, rsa_key->e.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    err = gcry_sexp_build(&rsa->key, NULL,
> +                          "(public-key (rsa (n %m) (e %m)))", n, e);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa, n);
> +    ret = 0;
> +
> +cleanup:
> +    gcry_mpi_release(n);
> +    gcry_mpi_release(e);
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_rsa_encrypt(QCryptoAkCipher *akcipher,
> +                                      const void *in, size_t in_len,
> +                                      void *out, size_t out_len,
> +                                      Error **errp)
> +{
> +    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
> +    int ret = -1;
> +    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
> +    gcry_sexp_t cipher_sexp_item = NULL;
> +    gcry_mpi_t cipher_mpi = NULL;
> +    const char *result;
> +    gcry_error_t err;
> +    size_t actual_len;
> +
> +    if (in_len > akcipher->max_plaintext_len) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }
> +
> +    err = gcry_sexp_build(&data_sexp, NULL,
> +                          "(data (flags %s) (value %b))",
> +                          QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg),
> +                          in_len, in);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

We've got an 'Error **errp' parameter in this methood, but we're
failing to set it here and in other failure scenarios below.

> +
> +    err = gcry_pk_encrypt(&cipher_sexp, data_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    /* S-expression of cipher: (enc-val (rsa (a a-mpi))) */
> +    cipher_sexp_item = gcry_sexp_find_token(cipher_sexp, "a", 0);
> +    if (!cipher_sexp_item || gcry_sexp_length(cipher_sexp_item) != 2) {
> +        goto cleanup;
> +    }
> +
> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
> +        cipher_mpi = gcry_sexp_nth_mpi(cipher_sexp_item, 1, GCRYMPI_FMT_USG);
> +        if (!cipher_mpi) {
> +            goto cleanup;
> +        }
> +        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
> +                             &actual_len, cipher_mpi);
> +        if (gcry_err_code(err) != 0 || actual_len > out_len) {
> +            goto cleanup;
> +        }
> +
> +        /* We always padding leading-zeros for RSA-RAW */
> +        if (actual_len < out_len) {
> +            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
> +            memset(out, 0, out_len - actual_len);
> +        }
> +        ret = out_len;
> +
> +    } else {
> +        result = gcry_sexp_nth_data(cipher_sexp_item, 1, &actual_len);
> +        if (!result || actual_len > out_len) {
> +            goto cleanup;
> +        }
> +        memcpy(out, result, actual_len);
> +        ret = actual_len;
> +    }
> +
> +cleanup:
> +    gcry_sexp_release(data_sexp);
> +    gcry_sexp_release(cipher_sexp);
> +    gcry_sexp_release(cipher_sexp_item);
> +    gcry_mpi_release(cipher_mpi);
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_rsa_decrypt(QCryptoAkCipher *akcipher,
> +                                      const void *in, size_t in_len,
> +                                      void *out, size_t out_len,
> +                                      Error **errp)
> +{
> +    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
> +    int ret = -1;
> +    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
> +    gcry_mpi_t data_mpi = NULL;
> +    gcry_error_t err;
> +    size_t actual_len;
> +    const char *result;
> +
> +    if (in_len > akcipher->max_ciphertext_len) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }
> +
> +    err = gcry_sexp_build(&cipher_sexp, NULL,
> +                          "(enc-val (flags %s) (rsa (a %b) ))",
> +                          QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg),
> +                          in_len, in);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

Again failing to set 'Error **errp'

> +
> +    err = gcry_pk_decrypt(&data_sexp, cipher_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    /* S-expression of cipher: (value plaintext) */
> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
> +        data_mpi = gcry_sexp_nth_mpi(data_sexp, 1, GCRYMPI_FMT_USG);
> +        if (!data_mpi) {
> +            goto cleanup;
> +        }
> +        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
> +                             &actual_len, data_mpi);
> +        if (gcry_err_code(err) != 0) {
> +            goto cleanup;
> +        }
> +         if (actual_len > out_len) {
> +            goto cleanup;
> +        }
> +        /* We always padding leading-zeros for RSA-RAW */
> +        if (actual_len < out_len) {
> +            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
> +            memset(out, 0, out_len - actual_len);
> +        }
> +        ret = out_len;
> +    } else {
> +        result = gcry_sexp_nth_data(data_sexp, 1, &actual_len);
> +        if (!result || actual_len > out_len) {
> +            goto cleanup;
> +        }
> +        memcpy(out, result, actual_len);
> +        ret = actual_len;
> +    }
> +
> +cleanup:
> +    gcry_sexp_release(cipher_sexp);
> +    gcry_sexp_release(data_sexp);
> +    gcry_mpi_release(data_mpi);
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_rsa_sign(QCryptoAkCipher *akcipher,
> +                                   const void *in, size_t in_len,
> +                                   void *out, size_t out_len, Error **errp)
> +{
> +    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
> +    int ret = -1;
> +    gcry_sexp_t dgst_sexp = NULL, sig_sexp = NULL;
> +    gcry_sexp_t sig_sexp_item = NULL;
> +    const char *result;
> +    gcry_error_t err;
> +    size_t actual_len;
> +
> +    if (in_len > akcipher->max_dgst_len) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }
> +
> +    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) {
> +        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
> +        return ret;
> +    }
> +    err = gcry_sexp_build(&dgst_sexp, NULL,
> +                          "(data (flags pkcs1) (hash %s %b))",
> +                          QCryptoHashAlgorithm_str(rsa->hash_alg),
> +                          in_len, in);
> +
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

Again failing to set 'Error **errp'


> +
> +    err = gcry_pk_sign(&sig_sexp, dgst_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    /* S-expression of signature: (sig-val (rsa (s s-mpi))) */
> +    sig_sexp_item = gcry_sexp_find_token(sig_sexp, "s", 0);
> +    if (!sig_sexp_item || gcry_sexp_length(sig_sexp_item) != 2) {
> +        goto cleanup;
> +    }
> +
> +    result = gcry_sexp_nth_data(sig_sexp_item, 1, &actual_len);
> +    if (!result || actual_len > out_len) {
> +        goto cleanup;
> +    }
> +    memcpy(out, result, actual_len);
> +    ret = actual_len;
> +
> +cleanup:
> +    gcry_sexp_release(dgst_sexp);
> +    gcry_sexp_release(sig_sexp);
> +    gcry_sexp_release(sig_sexp_item);
> +
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_rsa_verify(QCryptoAkCipher *akcipher,
> +                                     const void *in, size_t in_len,
> +                                     const void *in2, size_t in2_len,
> +                                     Error **errp)
> +{
> +    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
> +    int ret = -1;
> +    gcry_sexp_t sig_sexp = NULL, dgst_sexp = NULL;
> +    gcry_error_t err;
> +
> +    if (in_len > akcipher->max_signature_len ||
> +        in2_len > akcipher->max_dgst_len) {
> +        error_setg(errp, "Invalid buffer size");
> +        return ret;
> +    }
> +
> +    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) {
> +        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
> +        return ret;
> +    }
> +
> +    err = gcry_sexp_build(&sig_sexp, NULL,
> +                          "(sig-val (rsa (s %b)))", in_len, in);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }

Again failing to set 'Error **errp'


> +
> +    err = gcry_sexp_build(&dgst_sexp, NULL,
> +                          "(data (flags pkcs1) (hash %s %b))",
> +                          QCryptoHashAlgorithm_str(rsa->hash_alg),
> +                          in2_len, in2);
> +    if (gcry_err_code(err) != 0) {
> +        goto cleanup;
> +    }
> +
> +    err = gcry_pk_verify(sig_sexp, dgst_sexp, rsa->key);
> +    if (gcry_err_code(err) == 0) {
> +        ret = 0;
> +    }
> +
> +cleanup:
> +    gcry_sexp_release(dgst_sexp);
> +    gcry_sexp_release(sig_sexp);
> +
> +    return ret;
> +}
> +
> +QCryptoAkCipherDriver gcrypt_rsa = {
> +    .encrypt = qcrypto_gcrypt_rsa_encrypt,
> +    .decrypt = qcrypto_gcrypt_rsa_decrypt,
> +    .sign = qcrypto_gcrypt_rsa_sign,
> +    .verify = qcrypto_gcrypt_rsa_verify,
> +    .free = qcrypto_gcrypt_rsa_free,
> +};
> +
> +static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
> +    const QCryptoAkCipherOptionsRSA *opt,
> +    QCryptoAkCipherKeyType type,
> +    const uint8_t *key, size_t keylen,
> +    Error **errp)
> +{
> +    QCryptoGcryptRSA *rsa = g_new0(QCryptoGcryptRSA, 1);
> +    rsa->padding_alg = opt->padding_alg;
> +    rsa->hash_alg = opt->hash_alg;
> +    rsa->akcipher.driver = &gcrypt_rsa;
> +
> +    switch (type) {
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> +        if (qcrypto_gcrypt_parse_rsa_private_key(rsa, key, keylen) != 0) {
> +            error_setg(errp, "Failed to parse rsa private key");
> +            goto error;
> +        }
> +        break;
> +
> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> +        if (qcrypto_gcrypt_parse_rsa_public_key(rsa, key, keylen) != 0) {
> +            error_setg(errp, "Failed to parse rsa public rsa key");
> +            goto error;
> +        }
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown akcipher key type %d", type);
> +        goto error;
> +    }
> +
> +    return rsa;
> +
> +error:
> +    qcrypto_gcrypt_rsa_free((QCryptoAkCipher *)rsa);
> +    return NULL;
> +}
> +
> +
> +bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
> +{
> +    switch (opts->alg) {
> +    case QCRYPTO_AKCIPHER_ALG_RSA:
> +        switch (opts->u.rsa.padding_alg) {
> +        case QCRYPTO_RSA_PADDING_ALG_RAW:
> +            return true;
> +
> +        case QCRYPTO_RSA_PADDING_ALG_PKCS1:
> +            switch (opts->u.rsa.hash_alg) {
> +            case QCRYPTO_HASH_ALG_MD5:
> +            case QCRYPTO_HASH_ALG_SHA1:
> +            case QCRYPTO_HASH_ALG_SHA256:
> +            case QCRYPTO_HASH_ALG_SHA512:
> +                return true;
> +
> +            default:
> +                return false;
> +            }
> +
> +        default:
> +            return false;
> +        }
> +
> +    default:
> +        return true;
> +    }
> +}
> diff --git a/crypto/akcipher.c b/crypto/akcipher.c
> index f287083f92..ad88379c1e 100644
> --- a/crypto/akcipher.c
> +++ b/crypto/akcipher.c
> @@ -23,7 +23,9 @@
>  #include "crypto/akcipher.h"
>  #include "akcipherpriv.h"
>  
> -#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
> +#if defined(CONFIG_GCRYPT)
> +#include "akcipher-gcrypt.c.inc"
> +#elif defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
>  #include "akcipher-nettle.c.inc"
>  #else
>  QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
> -- 
> 2.20.1
> 

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [External] [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed
  2022-05-13 10:55   ` Daniel P. Berrangé
@ 2022-05-13 12:26     ` 何磊
  2022-05-13 12:29       ` Daniel P. Berrangé
  0 siblings, 1 reply; 23+ messages in thread
From: 何磊 @ 2022-05-13 12:26 UTC (permalink / raw)
  To: "Daniel P. Berrangé"
  Cc: 何磊,
	zhenwei pi, S. Tsirkin, Michael, arei.gonglei, qemu-devel,
	virtualization, linux-crypto, jasowang, cohuck



> On May 13, 2022, at 6:55 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> 
> On Thu, Apr 28, 2022 at 09:59:39PM +0800, zhenwei pi wrote:
>> From: Lei He <helei.sig11@bytedance.com>
>> 
>> Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
>> a 'real' RSA backend to handle request from guest side. It's
>> important to test RSA offload case without OS & hardware requirement.
>> 
>> Signed-off-by: lei he <helei.sig11@bytedance.com>
>> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
>> ---
>> crypto/akcipher-nettle.c.inc | 432 +++++++++++++++++++++++++++++++++++
>> crypto/akcipher.c            |   4 +
>> crypto/meson.build           |   4 +
>> crypto/rsakey-builtin.c.inc  | 209 +++++++++++++++++
>> crypto/rsakey-nettle.c.inc   | 154 +++++++++++++
>> crypto/rsakey.c              |  44 ++++
>> crypto/rsakey.h              |  94 ++++++++
>> meson.build                  |  11 +
>> 8 files changed, 952 insertions(+)
>> create mode 100644 crypto/akcipher-nettle.c.inc
>> create mode 100644 crypto/rsakey-builtin.c.inc
>> create mode 100644 crypto/rsakey-nettle.c.inc
>> create mode 100644 crypto/rsakey.c
>> create mode 100644 crypto/rsakey.h
>> 
> 
> 
>> +
>> +static void wrap_nettle_random_func(void *ctx, size_t len, uint8_t *out)
>> +{
>> +    /* TODO: check result */
>> +    qcrypto_random_bytes(out, len, NULL);
>> +}
> 
> Unfortunate meson requires this function to be void.
> 
> Since we've no way to report errors, then our only option is assume
> qcrypto_random_bytes will never fail, and enforce that assumptiomn
> by passing '&error_abort' for the last parameter.
> 
>> +
>> +static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher,
>> +                                      const void *data, size_t data_len,
>> +                                      void *enc, size_t enc_len,
>> +                                      Error **errp)
>> +{
>> +
>> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
>> +    mpz_t c;
>> +    int ret = -1;
>> +
>> +    if (data_len > rsa->pub.size || enc_len != rsa->pub.size) {
>> +        error_setg(errp, "Invalid buffer size");
>> +        return ret;
>> +    }
> 
> Can you report the invalid & expect buffer sizes, as it'll
> make debugging much easier. You'll need a separate check
> and error reporting for enc_len and data_len.

In addition, 'enc_len != rsa->pub.size' should be 'enc_len < rsa->pub.size', I
will fix this later.

> 
>> +
>> +    /* Nettle do not support RSA encryption without any padding */
>> +    switch (rsa->padding_alg) {
>> +    case QCRYPTO_RSA_PADDING_ALG_RAW:
>> +        error_setg(errp, "RSA with raw padding is not supported");
>> +        break;
>> +
>> +    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
>> +        mpz_init(c);
>> +        if (rsa_encrypt(&rsa->pub, NULL, wrap_nettle_random_func,
>> +                          data_len, (uint8_t *)data, c) != 1) {
>> +            error_setg(errp, "Failed to encrypt");
>> +        } else {
>> +            nettle_mpz_get_str_256(enc_len, (uint8_t *)enc, c);
>> +            ret = enc_len;
>> +        }
>> +        mpz_clear(c);
>> +        break;
>> +
>> +    default:
>> +        error_setg(errp, "Unknown padding");
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,
>> +                                      const void *enc, size_t enc_len,
>> +                                      void *data, size_t data_len,
>> +                                      Error **errp)
>> +{
>> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
>> +    mpz_t c;
>> +    int ret = -1;
>> +    if (enc_len > rsa->priv.size) {
>> +        error_setg(errp, "Invalid buffer size");
>> +        return ret;
>> +    }
> 
> Again please report the invalid & expected sizes in the message
> 
> We don't need to validate 'data_len' in the decrypt case,
> as you did in encrypt ?

In the decrypt case, it is difficult (and unnecessary) to check 'data_len' before 
we completing the decryption action. If the plaintext buffer is too small, 
following ‘rsa_decrypt’ will return an error, and it should be valid to pass a very 
large buffer.

According to the pkcs#1 stardard, the length of ciphertext should always equal
to key size, and the length of plaintext can be any value in range [1, key_size - 11]:

https://datatracker.ietf.org/doc/html/rfc2437#section-7.2  

> 
>> +
>> +    switch (rsa->padding_alg) {
>> +    case QCRYPTO_RSA_PADDING_ALG_RAW:
>> +        error_setg(errp, "RSA with raw padding is not supported");
>> +        break;
>> +
>> +    case QCRYPTO_RSA_PADDING_ALG_PKCS1:
>> +        nettle_mpz_init_set_str_256_u(c, enc_len, enc);
>> +        if (!rsa_decrypt(&rsa->priv, &data_len, (uint8_t *)data, c)) {
>> +            error_setg(errp, "Failed to decrypt");
>> +        } else {
>> +            ret = data_len;
>> +        }
>> +
>> +        mpz_clear(c);
>> +        break;
>> +
>> +    default:
>> +        ret = -1;
> 
> 'ret' was initialized to '-1' at time of declaration
> 
>> +        error_setg(errp, "Unknown padding");
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher,
>> +                                   const void *data, size_t data_len,
>> +                                   void *sig, size_t sig_len, Error **errp)
>> +{
>> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
>> +    int ret;
> 
> For consistency with the earlier methods, initialize this to -1
> 
>> +    mpz_t s;
>> +
>> +    /**
>> +     * The RSA algorithm cannot be used for signature/verification
>> +     * without padding.
>> +     */
>> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
>> +        error_setg(errp, "Try to make signature without padding");
>> +        return -1;
>> +    }
>> +
>> +    if (data_len > rsa->priv.size || sig_len != rsa->priv.size) {
>> +        error_setg(errp, "Invalid buffer size");
>> +        return -1;
>> +    }
> 
> Same note about reporting the lengths.
> 
>> +
>> +    mpz_init(s);
>> +    switch (rsa->hash_alg) {
>> +    case QCRYPTO_HASH_ALG_MD5:
>> +        ret = rsa_md5_sign_digest(&rsa->priv, data, s);
> 
> I'd suggest using a separate variable 'rv' here, as I
> find it can be confusing to re-use a variable for two
> different purposes. Keep 'ret' exclusively for holdnig
> the method return value.
> 
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA1:
>> +        ret = rsa_sha1_sign_digest(&rsa->priv, data, s);
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA256:
>> +        ret = rsa_sha256_sign_digest(&rsa->priv, data, s);
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA512:
>> +        ret = rsa_sha512_sign_digest(&rsa->priv, data, s);
>> +        break;
>> +
>> +    default:
>> +        error_setg(errp, "Unknown hash algorithm");
>> +        ret = -1;
> 
> No need if we initialize 'ret' upfront.
> 
>> +        goto cleanup;
>> +    }
>> +
>> +    if (ret != 1) {
>> +        error_setg(errp, "Failed to make signature");
>> +        ret = -1;
>> +        goto cleanup;
>> +    }
>> +    nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
>> +    ret = sig_len;
>> +
>> +cleanup:
>> +    mpz_clear(s);
>> +
>> +    return ret;
>> +}
>> +
>> +static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher,
>> +                                     const void *sig, size_t sig_len,
>> +                                     const void *data, size_t data_len,
>> +                                     Error **errp)
>> +{
>> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
>> +
>> +    int ret;
> 
> Initialize to -1 here.
> 
>> +    mpz_t s;
>> +
>> +    /**
>> +     * The RSA algorithm cannot be used for signature/verification
>> +     * without padding.
>> +     */
>> +    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) {
>> +        error_setg(errp, "Operation not supported");
>> +        return -1;
>> +    }
>> +    if (data_len > rsa->pub.size || sig_len < rsa->pub.size) {
>> +        error_setg(errp, "Invalid buffer size");
>> +        return -1;
>> +    }
> 
> Ssame note as earlier methods
> 
>> +
>> +    nettle_mpz_init_set_str_256_u(s, sig_len, sig);
>> +    switch (rsa->hash_alg) {
>> +    case QCRYPTO_HASH_ALG_MD5:
>> +        ret = rsa_md5_verify_digest(&rsa->pub, data, s);
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA1:
>> +        ret = rsa_sha1_verify_digest(&rsa->pub, data, s);
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA256:
>> +        ret = rsa_sha256_verify_digest(&rsa->pub, data, s);
>> +        break;
>> +
>> +    case QCRYPTO_HASH_ALG_SHA512:
>> +        ret = rsa_sha512_verify_digest(&rsa->pub, data, s);
>> +        break;
>> +
>> +    default:
>> +        error_setg(errp, "Unsupported hash algorithm");
>> +        ret = -1;
> 
> Skip this
> 
>> +        goto cleanup;
>> +    }
>> +
>> +    if (ret != 1) {
>> +        error_setg(errp, "Failed to verify");
>> +        ret = -1;
>> +        goto cleanup;
>> +    }
>> +    ret = 0;
>> +
>> +cleanup:
>> +    mpz_clear(s);
>> +
>> +    return ret;
>> +}
>> +
>> +QCryptoAkCipherDriver nettle_rsa = {
>> +    .encrypt = qcrypto_nettle_rsa_encrypt,
>> +    .decrypt = qcrypto_nettle_rsa_decrypt,
>> +    .sign = qcrypto_nettle_rsa_sign,
>> +    .verify = qcrypto_nettle_rsa_verify,
>> +    .free = qcrypto_nettle_rsa_free,
>> +};
>> +
>> +static QCryptoAkCipher *qcrypto_nettle_rsa_new(
>> +    const QCryptoAkCipherOptionsRSA *opt,
>> +    QCryptoAkCipherKeyType type,
>> +    const uint8_t *key, size_t keylen,
>> +    Error **errp)
>> +{
>> +    QCryptoNettleRSA *rsa = g_new0(QCryptoNettleRSA, 1);
>> +
>> +    rsa->padding_alg = opt->padding_alg;
>> +    rsa->hash_alg = opt->hash_alg;
>> +    rsa->akcipher.driver = &nettle_rsa;
>> +    rsa_public_key_init(&rsa->pub);
>> +    rsa_private_key_init(&rsa->priv);
>> +
>> +    switch (type) {
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
>> +        if (qcrypt_nettle_parse_rsa_private_key(rsa, key, keylen) != 0) {
>> +            error_setg(errp, "Failed to parse rsa private key");
>> +            goto error;
>> +        }
>> +        break;
>> +
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
>> +        if (qcrypt_nettle_parse_rsa_public_key(rsa, key, keylen) != 0) {
>> +            error_setg(errp, "Failed to parse rsa public rsa key");
>> +            goto error;
>> +        }
>> +        break;
>> +
>> +    default:
>> +        error_setg(errp, "Unknown akcipher key type %d", type);
>> +        goto error;
>> +    }
>> +
>> +    return (QCryptoAkCipher *)rsa;
>> +
>> +error:
>> +    qcrypto_nettle_rsa_free((QCryptoAkCipher *)rsa);
>> +    return NULL;
>> +}
>> +
>> +
>> +bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
>> +{
>> +    switch (opts->alg) {
>> +    case QCRYPTO_AKCIPHER_ALG_RSA:
>> +        switch (opts->u.rsa.padding_alg) {
>> +        case QCRYPTO_RSA_PADDING_ALG_PKCS1:
>> +            switch (opts->u.rsa.hash_alg) {
>> +            case QCRYPTO_HASH_ALG_MD5:
>> +            case QCRYPTO_HASH_ALG_SHA1:
>> +            case QCRYPTO_HASH_ALG_SHA256:
>> +            case QCRYPTO_HASH_ALG_SHA512:
>> +                return true;
>> +
>> +            default:
>> +                return false;
>> +            }
>> +
>> +        case QCRYPTO_RSA_PADDING_ALG_RAW:
>> +        default:
>> +            return false;
>> +        }
>> +        break;
>> +
>> +    default:
>> +        return false;
>> +    }
>> +}
>> diff --git a/crypto/akcipher.c b/crypto/akcipher.c
>> index ab28bf415b..f287083f92 100644
>> --- a/crypto/akcipher.c
>> +++ b/crypto/akcipher.c
>> @@ -23,6 +23,9 @@
>> #include "crypto/akcipher.h"
>> #include "akcipherpriv.h"
>> 
>> +#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
>> +#include "akcipher-nettle.c.inc"
>> +#else
>> QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
>>                                       QCryptoAkCipherKeyType type,
>>                                       const uint8_t *key, size_t keylen,
>> @@ -37,6 +40,7 @@ bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
>> {
>>     return false;
>> }
>> +#endif
>> 
>> int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
>>                              const void *in, size_t in_len,
>> diff --git a/crypto/meson.build b/crypto/meson.build
>> index c9b36857a6..d9294eed83 100644
>> --- a/crypto/meson.build
>> +++ b/crypto/meson.build
>> @@ -21,10 +21,14 @@ crypto_ss.add(files(
>>   'tlscredspsk.c',
>>   'tlscredsx509.c',
>>   'tlssession.c',
>> +  'rsakey.c',
>> ))
>> 
>> if nettle.found()
>>   crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c'))
>> +  if hogweed.found()
>> +    crypto_ss.add(gmp, hogweed)
>> +  endif
>>   if xts == 'private'
>>     crypto_ss.add(files('xts.c'))
>>   endif
>> diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc
>> new file mode 100644
>> index 0000000000..0a93712f4f
>> --- /dev/null
>> +++ b/crypto/rsakey-builtin.c.inc
>> @@ -0,0 +1,209 @@
>> +/*
>> + * QEMU Crypto akcipher algorithms
>> + *
>> + * Copyright (c) 2022 Bytedance
>> + * Author: lei he <helei.sig11@bytedance.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#include "der.h"
>> +#include "rsakey.h"
>> +
>> +static int extract_mpi(void *ctx, const uint8_t *value,
>> +                       size_t vlen, Error **errp)
>> +{
>> +    QCryptoAkCipherMPI *mpi = (QCryptoAkCipherMPI *)ctx;
>> +    if (vlen == 0) {
>> +        error_setg(errp, "Empty mpi field");
>> +        return -1;
>> +    }
>> +    mpi->data = g_memdup2(value, vlen);
>> +    mpi->len = vlen;
>> +    return 0;
>> +}
>> +
>> +static int extract_version(void *ctx, const uint8_t *value,
>> +                           size_t vlen, Error **errp)
>> +{
>> +    uint8_t *version = (uint8_t *)ctx;
>> +    if (vlen != 1 || *value > 1) {
>> +        error_setg(errp, "Invalid rsakey version");
>> +        return -1;
>> +    }
>> +    *version = *value;
>> +    return 0;
>> +}
>> +
>> +static int extract_seq_content(void *ctx, const uint8_t *value,
>> +                               size_t vlen, Error **errp)
>> +{
>> +    const uint8_t **content = (const uint8_t **)ctx;
>> +    if (vlen == 0) {
>> +        error_setg(errp, "Empty sequence");
>> +        return -1;
>> +    }
>> +    *content = value;
>> +    return 0;
>> +}
>> +
>> +/**
>> + *
>> + *        RsaPubKey ::= SEQUENCE {
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *         }
>> + */
>> +static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse(
>> +    const uint8_t *key, size_t keylen)
>> +{
>> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
>> +    const uint8_t *seq;
>> +    size_t seq_length;
>> +    int decode_ret;
>> +    Error *local_error = NULL;
>> +
>> +    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
>> +        extract_seq_content, &seq, &local_error);
>> +    if (decode_ret < 0) {
>> +        error_report_err(local_error);
> 
> Nothing in the crypto/ directory should ever call error_report_err.
> Any methods  which can fail need to have an 'Error **errp' parameter
> and propagate this back to the caller(s).
> 
>> +        goto error;
>> +    }
>> +    if (keylen != 0) {
>> +        goto error;
>> +    }
>> +    seq_length = decode_ret;
>> +
>> +    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
>> +                               &rsa->n, &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
>> +                               &rsa->e, &local_error) < 0) {
>> +        error_report_err(local_error);
>> +        goto error;
>> +    }
>> +    if (seq_length != 0) {
>> +        goto error;
>> +    }
>> +
>> +    return rsa;
>> +
>> +error:
>> +    qcrypto_akcipher_rsakey_free(rsa);
>> +    return NULL;
>> +}
>> +
>> +/**
>> + *        RsaPrivKey ::= SEQUENCE {
>> + *             version     INTEGER
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *             d           INTEGER
>> + *             p           INTEGER
>> + *             q           INTEGER
>> + *             dp          INTEGER
>> + *             dq          INTEGER
>> + *             u           INTEGER
>> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
>> + *         }
>> + */
>> +static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse(
>> +    const uint8_t *key, size_t keylen)
>> +{
>> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
>> +    uint8_t version;
>> +    const uint8_t *seq;
>> +    int decode_ret;
>> +    size_t seq_length;
>> +    Error *local_error = NULL;
>> +
>> +    decode_ret = qcrypto_der_decode_seq(&key, &keylen,
>> +        extract_seq_content, &seq, &local_error);
>> +    if (decode_ret < 0) {
>> +        error_report_err(local_error);
>> +        goto error;
>> +    }
>> +    if (keylen != 0) {
>> +        goto error;
>> +    }
>> +    seq_length = decode_ret;
>> +
>> +    decode_ret = qcrypto_der_decode_int(&seq, &seq_length, extract_version,
>> +                                        &version, &local_error);
>> +    if (decode_ret < 0) {
>> +        error_report_err(local_error);
>> +        goto error;
>> +    }
>> +
>> +    if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
>> +                               &rsa->n, &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
>> +                               &rsa->e, &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
>> +                               &rsa->d, &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p,
>> +                               &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q,
>> +                               &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp,
>> +                               &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq,
>> +                               &local_error) < 0 ||
>> +        qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u,
>> +                               &local_error) < 0) {
>> +        error_report_err(local_error);
>> +        goto error;
>> +    }
>> +    /**
>> +     * According to the standard, otherPrimeInfos must be present for version 1.
>> +     * There is no strict verification here, this is to be compatible with
>> +     * the unit test of the kernel. TODO: remove this until linux kernel's
>> +     * unit-test is fixed.
>> +     */
>> +    if (version == 1 && seq_length != 0) {
>> +        if (qcrypto_der_decode_seq(&seq, &seq_length,
>> +                                   NULL, NULL, &local_error) < 0) {
>> +            error_report_err(local_error);
>> +            goto error;
>> +        }
>> +        if (seq_length != 0) {
>> +            goto error;
>> +        }
>> +        return rsa;
>> +    }
>> +    if (seq_length != 0) {
>> +        goto error;
>> +    }
>> +
>> +    return rsa;
>> +
>> +error:
>> +    qcrypto_akcipher_rsakey_free(rsa);
>> +    return NULL;
>> +}
>> +
>> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
>> +    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
>> +{
>> +    switch (type) {
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
>> +        return qcrypto_builtin_rsa_private_key_parse(key, keylen);
>> +
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
>> +        return qcrypto_builtin_rsa_public_key_parse(key, keylen);
>> +
>> +    default:
>> +        return NULL;
>> +    }
>> +}
>> diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
>> new file mode 100644
>> index 0000000000..2c89b3be88
>> --- /dev/null
>> +++ b/crypto/rsakey-nettle.c.inc
>> @@ -0,0 +1,154 @@
>> +/*
>> + * QEMU Crypto akcipher algorithms
>> + *
>> + * Copyright (c) 2022 Bytedance
>> + * Author: lei he <helei.sig11@bytedance.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#include <nettle/asn1.h>
>> +#include <stdbool.h>
>> +
>> +#include "rsakey.h"
>> +
>> +static bool DumpMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
>> +{
>> +    mpi->data = g_memdup2(i->data, i->length);
>> +    mpi->len = i->length;
>> +    return true;
>> +}
>> +
>> +static bool GetMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
>> +{
>> +    if (asn1_der_iterator_next(i) != ASN1_ITERATOR_PRIMITIVE ||
>> +        i->type != ASN1_INTEGER) {
>> +        return false;
>> +    }
>> +    return DumpMPI(i, mpi);
>> +}
>> +
>> +
>> +/**
>> + *        RsaPrivKey ::= SEQUENCE {
>> + *             version     INTEGER
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *             d           INTEGER
>> + *             p           INTEGER
>> + *             q           INTEGER
>> + *             dp          INTEGER
>> + *             dq          INTEGER
>> + *             u           INTEGER
>> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
>> + *         }
>> + */
>> +static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_private_key_parse(
>> +    const uint8_t *key, size_t keylen)
>> +{
>> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
>> +    struct asn1_der_iterator i;
>> +    uint32_t version;
>> +    int tag;
>> +
>> +    /* Parse entire struct */
>> +    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
>> +        i.type != ASN1_SEQUENCE ||
>> +        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
>> +        i.type != ASN1_INTEGER ||
>> +        !asn1_der_get_uint32(&i, &version) ||
>> +        version > 1 ||
>> +        !GetMPI(&i, &rsa->n) ||
>> +        !GetMPI(&i, &rsa->e) ||
>> +        !GetMPI(&i, &rsa->d) ||
>> +        !GetMPI(&i, &rsa->p) ||
>> +        !GetMPI(&i, &rsa->q) ||
>> +        !GetMPI(&i, &rsa->dp) ||
>> +        !GetMPI(&i, &rsa->dq) ||
>> +        !GetMPI(&i, &rsa->u)) {
>> +        goto error;
>> +    }
>> +
>> +    if (version == 1) {
>> +        tag = asn1_der_iterator_next(&i);
>> +        /**
>> +         * According to the standard otherPrimeInfos must be present for
>> +         * version 1. There is no strict verification here, this is to be
>> +         * compatible with the unit test of the kernel. TODO: remove this
>> +         * until linux-kernel's unit-test is fixed;
>> +         */
>> +        if (tag == ASN1_ITERATOR_END) {
>> +            return rsa;
>> +        }
>> +        if (tag != ASN1_ITERATOR_CONSTRUCTED ||
>> +            i.type != ASN1_SEQUENCE) {
>> +            goto error;
>> +        }
>> +    }
>> +
>> +    if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
>> +        goto error;
>> +    }
>> +
>> +    return rsa;
>> +
>> +error:
>> +    qcrypto_akcipher_rsakey_free(rsa);
>> +    return NULL;
>> +}
>> +
>> +/**
>> + *        RsaPubKey ::= SEQUENCE {
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *         }
>> + */
>> +static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_public_key_parse(
>> +    const uint8_t *key, size_t keylen)
>> +{
>> +
>> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
>> +    struct asn1_der_iterator i;
>> +
>> +    if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
>> +        i.type != ASN1_SEQUENCE ||
>> +        asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
>> +        !DumpMPI(&i, &rsa->n) ||
>> +        !GetMPI(&i, &rsa->e) ||
>> +        asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
>> +        goto error;
>> +    }
>> +
>> +    return rsa;
>> +
>> +error:
>> +    qcrypto_akcipher_rsakey_free(rsa);
>> +    return NULL;
>> +}
>> +
>> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
>> +    QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen)
>> +{
>> +    switch (type) {
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
>> +        return qcrypto_nettle_rsa_private_key_parse(key, keylen);
>> +
>> +    case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
>> +        return qcrypto_nettle_rsa_public_key_parse(key, keylen);
>> +
>> +    default:
>> +        return NULL;
>> +    }
>> +}
>> diff --git a/crypto/rsakey.c b/crypto/rsakey.c
>> new file mode 100644
>> index 0000000000..cc40e072f0
>> --- /dev/null
>> +++ b/crypto/rsakey.c
>> @@ -0,0 +1,44 @@
>> +/*
>> + * QEMU Crypto RSA key parser
>> + *
>> + * Copyright (c) 2022 Bytedance
>> + * Author: lei he <helei.sig11@bytedance.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#include "rsakey.h"
>> +
>> +void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
>> +{
>> +    if (!rsa_key) {
>> +        return;
>> +    }
>> +    g_free(rsa_key->n.data);
>> +    g_free(rsa_key->e.data);
>> +    g_free(rsa_key->d.data);
>> +    g_free(rsa_key->p.data);
>> +    g_free(rsa_key->q.data);
>> +    g_free(rsa_key->dp.data);
>> +    g_free(rsa_key->dq.data);
>> +    g_free(rsa_key->u.data);
>> +    g_free(rsa_key);
>> +}
>> +
>> +#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
>> +#include "rsakey-nettle.c.inc"
>> +#else
>> +#include "rsakey-builtin.c.inc"
>> +#endif
>> diff --git a/crypto/rsakey.h b/crypto/rsakey.h
>> new file mode 100644
>> index 0000000000..a1e04ae021
>> --- /dev/null
>> +++ b/crypto/rsakey.h
>> @@ -0,0 +1,94 @@
>> +/*
>> + * QEMU Crypto RSA key parser
>> + *
>> + * Copyright (c) 2022 Bytedance
>> + * Author: lei he <helei.sig11@bytedance.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#ifndef QCRYPTO_RSAKEY_H
>> +#define QCRYPTO_RSAKEY_H
>> +
>> +#include <nettle/bignum.h>
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/host-utils.h"
>> +#include "crypto/akcipher.h"
>> +
>> +typedef struct QCryptoAkCipherRSAKey QCryptoAkCipherRSAKey;
>> +typedef struct QCryptoAkCipherMPI QCryptoAkCipherMPI;
>> +
>> +/**
>> + * Multiple precious integer, encoded as two' complement,
>> + * copied directly from DER encoded ASN.1 structures.
>> + */
>> +struct QCryptoAkCipherMPI {
>> +    uint8_t *data;
>> +    size_t len;
>> +};
>> +
>> +/* See rfc2437: https://datatracker.ietf.org/doc/html/rfc2437 */
>> +struct QCryptoAkCipherRSAKey {
>> +    /* The modulus */
>> +    QCryptoAkCipherMPI n;
>> +    /* The public exponent */
>> +    QCryptoAkCipherMPI e;
>> +    /* The private exponent */
>> +    QCryptoAkCipherMPI d;
>> +    /* The first factor */
>> +    QCryptoAkCipherMPI p;
>> +    /* The second factor */
>> +    QCryptoAkCipherMPI q;
>> +    /* The first factor's exponent */
>> +    QCryptoAkCipherMPI dp;
>> +    /* The second factor's exponent */
>> +    QCryptoAkCipherMPI dq;
>> +    /* The CRT coefficient */
>> +    QCryptoAkCipherMPI u;
>> +};
>> +
>> +/**
>> + * Parse DER encoded ASN.1 RSA keys, expected ASN.1 schemas:
>> + *        RsaPrivKey ::= SEQUENCE {
>> + *             version     INTEGER
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *             d           INTEGER
>> + *             p           INTEGER
>> + *             q           INTEGER
>> + *             dp          INTEGER
>> + *             dq          INTEGER
>> + *             u           INTEGER
>> + *       otherPrimeInfos   OtherPrimeInfos OPTIONAL
>> + *         }
>> + *
>> + *        RsaPubKey ::= SEQUENCE {
>> + *             n           INTEGER
>> + *             e           INTEGER
>> + *         }
>> + *
>> + * Returns: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
>> + */
>> +QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
>> +    QCryptoAkCipherKeyType type,
>> +    const uint8_t *key, size_t keylen);
>> +
>> +void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
>> +
>> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
>> +                              qcrypto_akcipher_rsakey_free);
>> +
>> +#endif
>> diff --git a/meson.build b/meson.build
>> index d083c6b7bf..fd0bf7aa5d 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -1049,6 +1049,7 @@ endif
>> # gcrypt over nettle for performance reasons.
>> gcrypt = not_found
>> nettle = not_found
>> +hogweed = not_found
>> xts = 'none'
>> 
>> if get_option('nettle').enabled() and get_option('gcrypt').enabled()
>> @@ -1086,6 +1087,15 @@ if not gnutls_crypto.found()
>>   endif
>> endif
>> 
>> +gmp = dependency('gmp', required: false, method: 'pkg-config', kwargs: static_kwargs)
>> +if nettle.found() and gmp.found()
>> +  hogweed = dependency('hogweed', version: '>=3.4',
>> +                       method: 'pkg-config',
>> +                       required: get_option('nettle'),
>> +                       kwargs: static_kwargs)
>> +endif
>> +
>> +
>> gtk = not_found
>> gtkx11 = not_found
>> vte = not_found
>> @@ -1567,6 +1577,7 @@ config_host_data.set('CONFIG_GNUTLS', gnutls.found())
>> config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found())
>> config_host_data.set('CONFIG_GCRYPT', gcrypt.found())
>> config_host_data.set('CONFIG_NETTLE', nettle.found())
>> +config_host_data.set('CONFIG_HOGWEED', hogweed.found())
>> config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
>> config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
>> config_host_data.set('CONFIG_STATX', has_statx)
>> -- 
>> 2.20.1
>> 
> 
> With regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [External] [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed
  2022-05-13 12:26     ` [External] " 何磊
@ 2022-05-13 12:29       ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2022-05-13 12:29 UTC (permalink / raw)
  To: 何磊
  Cc: zhenwei pi, S. Tsirkin, Michael, arei.gonglei, qemu-devel,
	virtualization, linux-crypto, jasowang, cohuck

On Fri, May 13, 2022 at 08:26:14PM +0800, 何磊 wrote:
> 
> 
> > On May 13, 2022, at 6:55 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> > 
> > On Thu, Apr 28, 2022 at 09:59:39PM +0800, zhenwei pi wrote:
> >> From: Lei He <helei.sig11@bytedance.com>
> >> 
> >> Implement RSA algorithm by hogweed from nettle. Thus QEMU supports
> >> a 'real' RSA backend to handle request from guest side. It's
> >> important to test RSA offload case without OS & hardware requirement.
> >> 
> >> Signed-off-by: lei he <helei.sig11@bytedance.com>
> >> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
> >> ---
> >> crypto/akcipher-nettle.c.inc | 432 +++++++++++++++++++++++++++++++++++
> >> crypto/akcipher.c            |   4 +
> >> crypto/meson.build           |   4 +
> >> crypto/rsakey-builtin.c.inc  | 209 +++++++++++++++++
> >> crypto/rsakey-nettle.c.inc   | 154 +++++++++++++
> >> crypto/rsakey.c              |  44 ++++
> >> crypto/rsakey.h              |  94 ++++++++
> >> meson.build                  |  11 +
> >> 8 files changed, 952 insertions(+)
> >> create mode 100644 crypto/akcipher-nettle.c.inc
> >> create mode 100644 crypto/rsakey-builtin.c.inc
> >> create mode 100644 crypto/rsakey-nettle.c.inc
> >> create mode 100644 crypto/rsakey.c
> >> create mode 100644 crypto/rsakey.h


> >> +static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,
> >> +                                      const void *enc, size_t enc_len,
> >> +                                      void *data, size_t data_len,
> >> +                                      Error **errp)
> >> +{
> >> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
> >> +    mpz_t c;
> >> +    int ret = -1;
> >> +    if (enc_len > rsa->priv.size) {
> >> +        error_setg(errp, "Invalid buffer size");
> >> +        return ret;
> >> +    }
> > 
> > Again please report the invalid & expected sizes in the message
> > 
> > We don't need to validate 'data_len' in the decrypt case,
> > as you did in encrypt ?
> 
> In the decrypt case, it is difficult (and unnecessary) to check 'data_len' before 
> we completing the decryption action. If the plaintext buffer is too small, 
> following ‘rsa_decrypt’ will return an error, and it should be valid to pass a very 
> large buffer.
> 
> According to the pkcs#1 stardard, the length of ciphertext should always equal
> to key size, and the length of plaintext can be any value in range [1, key_size - 11]:
> 
> https://datatracker.ietf.org/doc/html/rfc2437#section-7.2

Ok, thanks for explaining.


With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2022-05-13 12:29 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-28 13:59 [PATCH v5 0/9] Introduce akcipher service for virtio-crypto zhenwei pi
2022-04-28 13:59 ` [PATCH v5 1/9] virtio-crypto: header update zhenwei pi
2022-05-12  9:55   ` Daniel P. Berrangé
2022-05-13  3:50     ` zhenwei pi
2022-04-28 13:59 ` [PATCH v5 2/9] qapi: crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
2022-04-28 13:59 ` [PATCH v5 3/9] crypto: Introduce akcipher crypto class zhenwei pi
2022-05-12 10:04   ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 4/9] crypto: add ASN.1 DER decoder zhenwei pi
2022-05-12  9:46   ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 5/9] crypto: Implement RSA algorithm by hogweed zhenwei pi
2022-05-13 10:55   ` Daniel P. Berrangé
2022-05-13 12:26     ` [External] " 何磊
2022-05-13 12:29       ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 6/9] crypto: Implement RSA algorithm by gcrypt zhenwei pi
2022-05-13 11:00   ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 7/9] test/crypto: Add test suite for crypto akcipher zhenwei pi
2022-05-12  9:38   ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 8/9] tests/crypto: Add test suite for RSA keys zhenwei pi
2022-05-13 10:35   ` Daniel P. Berrangé
2022-04-28 13:59 ` [PATCH v5 9/9] crypto: Introduce RSA algorithm zhenwei pi
2022-05-09  2:17   ` PING: " zhenwei pi
2022-05-13 10:19 ` [PATCH v5 0/9] Introduce akcipher service for virtio-crypto Michael S. Tsirkin
2022-05-13 10:21   ` Michael S. Tsirkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).