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

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 (4):
  crypto-akcipher: Introduce akcipher types to qapi
  crypto: add ASN.1 decoder
  crypto: Implement RSA algorithm by hogweed
  crypto: Implement RSA algorithm by gcrypt

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

lei he (1):
  tests/crypto: Add test suite for crypto akcipher

 backends/cryptodev-builtin.c                  | 261 ++++++-
 backends/cryptodev-vhost-user.c               |  34 +-
 backends/cryptodev.c                          |  32 +-
 crypto/akcipher-gcrypt.c.inc                  | 531 +++++++++++++
 crypto/akcipher-nettle.c.inc                  | 448 +++++++++++
 crypto/akcipher.c                             | 108 +++
 crypto/akcipherpriv.h                         |  43 ++
 crypto/asn1_decoder.c                         | 161 ++++
 crypto/asn1_decoder.h                         |  75 ++
 crypto/meson.build                            |   6 +
 crypto/rsakey-builtin.c.inc                   | 150 ++++
 crypto/rsakey-nettle.c.inc                    | 141 ++++
 crypto/rsakey.c                               |  43 ++
 crypto/rsakey.h                               |  96 +++
 hw/virtio/virtio-crypto.c                     | 323 ++++++--
 include/crypto/akcipher.h                     | 151 ++++
 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       | 161 ++++
 tests/bench/meson.build                       |   4 +
 tests/bench/test_akcipher_keys.inc            | 537 +++++++++++++
 tests/unit/meson.build                        |   2 +
 tests/unit/test-crypto-akcipher.c             | 708 ++++++++++++++++++
 tests/unit/test-crypto-asn1-decoder.c         | 289 +++++++
 27 files changed, 4404 insertions(+), 145 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/asn1_decoder.c
 create mode 100644 crypto/asn1_decoder.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-asn1-decoder.c

-- 
2.20.1


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

* [PATCH v4 1/8] virtio-crypto: header update
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
@ 2022-04-11 10:43 ` zhenwei pi
  2022-04-11 10:43 ` [PATCH v4 2/8] crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: zhenwei pi @ 2022-04-11 10:43 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck,
	jasowang, 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] 19+ messages in thread

* [PATCH v4 2/8] crypto-akcipher: Introduce akcipher types to qapi
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
  2022-04-11 10:43 ` [PATCH v4 1/8] virtio-crypto: header update zhenwei pi
@ 2022-04-11 10:43 ` zhenwei pi
  2022-04-26 10:34   ` Daniel P. Berrangé
  2022-04-11 10:43 ` [PATCH v4 3/8] crypto: Introduce akcipher crypto class zhenwei pi
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: zhenwei pi @ 2022-04-11 10:43 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck,
	jasowang, zhenwei pi

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

Introduce akcipher types, also include RSA related types.

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..9e2b41fc82 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': { 'algorithm': 'QCryptoAkCipherAlgorithm' },
+  'discriminator': 'algorithm',
+  'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}
-- 
2.20.1


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

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

Support basic asymmetric operations: encrypt, decrypt, sign and
verify.

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     |  43 +++++++++++
 crypto/meson.build        |   1 +
 include/crypto/akcipher.h | 151 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 297 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..7323a48073
--- /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;
+}
+
+int qcrypto_akcipher_free(QCryptoAkCipher *akcipher, Error **errp)
+{
+    const QCryptoAkCipherDriver *drv = akcipher->driver;
+
+    return drv->free(akcipher, errp);
+}
diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h
new file mode 100644
index 0000000000..da9e54a796
--- /dev/null
+++ b/crypto/akcipherpriv.h
@@ -0,0 +1,43 @@
+/*
+ * 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"
+
+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);
+    int (*free)(QCryptoAkCipher *akcipher, Error **errp);
+};
+
+#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..c1970b3b3b
--- /dev/null
+++ b/include/crypto/akcipher.h
@@ -0,0 +1,151 @@
+/*
+ * 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;
+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;
+};
+
+/**
+ * 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 the plaintext, MUST less or equal to max_plaintext_len
+ * @out: buffer to store the ciphertext
+ * @out_len: the length of ciphertext buffer, usually equals to
+ *           max_ciphertext_len
+ * @errp: error pointer
+ *
+ * Encrypt data 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
+ * @out: buffer to store the plaintext
+ * @out_len: length of the plaintext buffer, usually less or equals to
+ *           max_plaintext_len
+ * @errp: error pointer
+ *
+ * Decrypt ciphertext 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
+ * @out: buffer to store the signature
+ * @out_len: length of the signature buffer, usually equals to max_signature_len
+ * @errp: error pointer
+ *
+ * Generate signature for data using akcipher
+ *
+ * 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 used to do verifycation
+ * @in: pointer to the signature
+ * @in_len: length of the signature
+ * @in2: pointer to original data
+ * @in2_len: the length of original data
+ * @errp: error pointer
+ *
+ * Verify the signature and the data 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);
+
+int qcrypto_akcipher_free(QCryptoAkCipher *akcipher, Error **errp);
+
+
+#endif /* QCRYPTO_AKCIPHER_H */
-- 
2.20.1


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

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

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

Add an ANS.1 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/asn1_decoder.c                 | 161 ++++++++++++++
 crypto/asn1_decoder.h                 |  75 +++++++
 crypto/meson.build                    |   1 +
 tests/unit/meson.build                |   1 +
 tests/unit/test-crypto-asn1-decoder.c | 289 ++++++++++++++++++++++++++
 5 files changed, 527 insertions(+)
 create mode 100644 crypto/asn1_decoder.c
 create mode 100644 crypto/asn1_decoder.h
 create mode 100644 tests/unit/test-crypto-asn1-decoder.c

diff --git a/crypto/asn1_decoder.c b/crypto/asn1_decoder.c
new file mode 100644
index 0000000000..506487f713
--- /dev/null
+++ b/crypto/asn1_decoder.c
@@ -0,0 +1,161 @@
+/*
+ * QEMU Crypto ASN.1 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/asn1_decoder.h"
+
+enum der_type_tag {
+    der_type_tag_bool = 0x1,
+    der_type_tag_int = 0x2,
+    der_type_tag_bit_str = 0x3,
+    der_type_tag_oct_str = 0x4,
+    der_type_tag_oct_null = 0x5,
+    der_type_tag_oct_oid = 0x6,
+    der_type_tag_seq = 0x10,
+    der_type_tag_set = 0x11,
+};
+
+#define DER_CONSTRUCTED_MASK 0x20
+#define DER_SHORT_LEN_MASK 0x80
+
+static uint8_t der_peek_byte(const uint8_t **data, size_t *dlen)
+{
+    return **data;
+}
+
+static void der_cut_nbytes(const uint8_t **data, size_t *dlen,
+                           size_t nbytes)
+{
+    *data += nbytes;
+    *dlen -= nbytes;
+}
+
+static uint8_t der_cut_byte(const uint8_t **data, size_t *dlen)
+{
+    uint8_t val = der_peek_byte(data, dlen);
+
+    der_cut_nbytes(data, dlen, 1);
+
+    return val;
+}
+
+static int der_invoke_callback(DERDecodeCb cb, void *ctx,
+                           const uint8_t *value, size_t vlen)
+{
+    if (!cb) {
+        return 0;
+    }
+
+    return cb(ctx, value, vlen);
+}
+
+static int der_extract_definite_data(const uint8_t **data, size_t *dlen,
+                                     DERDecodeCb cb, void *ctx)
+{
+    const uint8_t *value;
+    size_t vlen = 0;
+    uint8_t byte_count = der_cut_byte(data, dlen);
+
+    /* short format of definite-length */
+    if (!(byte_count & DER_SHORT_LEN_MASK)) {
+        if (byte_count > *dlen) {
+            return -1;
+        }
+
+        value = *data;
+        vlen = byte_count;
+        der_cut_nbytes(data, dlen, vlen);
+
+        if (der_invoke_callback(cb, ctx, value, vlen)) {
+            return -1;
+        }
+        return vlen;
+    }
+
+    /* Ignore highest bit */
+    byte_count &= ~DER_SHORT_LEN_MASK;
+
+    /*
+     * size_t is enough to express the length, although the der encoding
+     * standard supports larger length.
+     */
+    if (byte_count > sizeof(size_t)) {
+        return -1;
+    }
+
+    while (byte_count--) {
+        vlen <<= 8;
+        vlen += der_cut_byte(data, dlen);
+    }
+
+    if (vlen > *dlen) {
+        return -1;
+    }
+
+    value = *data;
+    der_cut_nbytes(data, dlen, vlen);
+
+    if (der_invoke_callback(cb, ctx, value, vlen) != 0) {
+        return -1;
+    }
+    return vlen;
+}
+
+static int der_extract_data(const uint8_t **data, size_t *dlen,
+                            DERDecodeCb cb, void *ctx)
+{
+    uint8_t val = der_peek_byte(data, dlen);
+
+    /* must use definite length format */
+    if (val == DER_SHORT_LEN_MASK) {
+        return -1;
+    }
+
+    return der_extract_definite_data(data, dlen, cb, ctx);
+}
+
+int der_decode_int(const uint8_t **data, size_t *dlen,
+                   DERDecodeCb cb, void *ctx)
+{
+    uint8_t tag = der_cut_byte(data, dlen);
+
+    /* INTEGER must encoded in primitive-form */
+    if (tag != der_type_tag_int) {
+        return -1;
+    }
+
+    return der_extract_data(data, dlen, cb, ctx);
+}
+
+int der_decode_seq(const uint8_t **data, size_t *dlen,
+                   DERDecodeCb cb, void *ctx)
+{
+    uint8_t val = der_cut_byte(data, dlen);
+
+    /* SEQUENCE must use constructed form */
+    if (val != (der_type_tag_seq | DER_CONSTRUCTED_MASK)) {
+        return -1;
+    }
+
+    return der_extract_data(data, dlen, cb, ctx);
+}
diff --git a/crypto/asn1_decoder.h b/crypto/asn1_decoder.h
new file mode 100644
index 0000000000..e414807af2
--- /dev/null
+++ b/crypto/asn1_decoder.h
@@ -0,0 +1,75 @@
+/*
+ * 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
+
+/* Simple decoder used to parse DER encoded rsa keys. */
+
+/**
+ *  @opaque: user content.
+ *  @value: the starting address of |value| part of 'Tag-Length-Value' pattern.
+ *  @vlen: length of the |value|.
+ */
+typedef int (*DERDecodeCb) (void *opaque, const uint8_t *value, size_t vlen);
+
+/**
+ * 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 der_decode_int(const uint8_t **data,
+                   size_t *dlen,
+                   DERDecodeCb cb,
+                   void *opaque);
+
+/**
+ * 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 der_decode_seq(const uint8_t **data,
+                   size_t *dlen,
+                   DERDecodeCb cb,
+                   void *opaque);
+
+#endif  /* QCRYPTO_ASN1_DECODER_H */
diff --git a/crypto/meson.build b/crypto/meson.build
index 7647d5e243..57f29d6f74 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -2,6 +2,7 @@ crypto_ss.add(genh)
 crypto_ss.add(files(
   'afsplit.c',
   'akcipher.c',
+  'asn1_decoder.c',
   'block-luks.c',
   'block-qcow.c',
   'block.c',
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 96b295263e..037ad0a0b9 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-asn1-decoder':[crypto],
 }
 
 if have_system or have_tools
diff --git a/tests/unit/test-crypto-asn1-decoder.c b/tests/unit/test-crypto-asn1-decoder.c
new file mode 100644
index 0000000000..16e10b12f9
--- /dev/null
+++ b/tests/unit/test-crypto-asn1-decoder.c
@@ -0,0 +1,289 @@
+/*
+ * 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/asn1_decoder.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);
+    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)
+{
+    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)
+            == checker->exp_vlen);
+        if (checker->action == 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/asn1-decoder/parse-rsa512-priv-key",
+    .test_data = test_rsa512_priv_key,
+    .test_data_len = sizeof(test_rsa512_priv_key) - 1,
+    .checker = {
+        { der_decode_seq, checker_callback,
+          test_rsa512_priv_key + 4, 313 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 4 + 2, 1 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 7 + 2, 65 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 74 + 2, 3 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 79 + 2, 64 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 145 + 2, 33 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 180 + 2, 33 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 215 + 2, 32 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 249 + 2, 32 },
+        { der_decode_int, checker_callback,
+          test_rsa512_priv_key + 283 + 2, 32 },
+    },
+},
+{
+    .path = "/crypto/asn1-decoder/parse-rsa2048-priv-key",
+    .test_data = test_rsa2048_priv_key,
+    .test_data_len = sizeof(test_rsa2048_priv_key) - 1,
+    .checker = {
+        { der_decode_seq, checker_callback,
+          test_rsa2048_priv_key + 4, 1190 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 4 + 2, 1 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 7 + 4, 257 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 268 + 2, 3 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 273 + 4, 257 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 534 + 3, 129 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 666 + 3, 129 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 798 + 3, 129 },
+        { der_decode_int, checker_callback,
+          test_rsa2048_priv_key + 930 + 3, 129 },
+        { 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] 19+ messages in thread

* [PATCH v4 5/8] crypto: Implement RSA algorithm by hogweed
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (3 preceding siblings ...)
  2022-04-11 10:43 ` [PATCH v4 4/8] crypto: add ASN.1 decoder zhenwei pi
@ 2022-04-11 10:43 ` zhenwei pi
  2022-04-26 11:51   ` Daniel P. Berrangé
  2022-04-11 10:43 ` [PATCH v4 6/8] crypto: Implement RSA algorithm by gcrypt zhenwei pi
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: zhenwei pi @ 2022-04-11 10:43 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck,
	jasowang, 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 | 448 +++++++++++++++++++++++++++++++++++
 crypto/akcipher.c            |   4 +
 crypto/meson.build           |   4 +
 crypto/rsakey-builtin.c.inc  | 150 ++++++++++++
 crypto/rsakey-nettle.c.inc   | 141 +++++++++++
 crypto/rsakey.c              |  43 ++++
 crypto/rsakey.h              |  96 ++++++++
 meson.build                  |  11 +
 8 files changed, 897 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..de163cd89e
--- /dev/null
+++ b/crypto/akcipher-nettle.c.inc
@@ -0,0 +1,448 @@
+/*
+ * 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 <stdbool.h>
+
+#include <nettle/rsa.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "asn1_decoder.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_destroy(void *ptr)
+{
+    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)ptr;
+    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->algorithm) {
+    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->algorithm);
+        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)
+{
+    QCryptoAkCipherRSAKey *rsa_key =
+        qcrypto_akcipher_parse_rsa_private_key(key, keylen);
+    int ret = -1;
+    if (!rsa_key) {
+        return ret;
+    }
+
+    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)) {
+        goto clear;
+    }
+
+    /**
+     * 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)) {
+            goto clear;
+        }
+    } else {
+        rsa->priv.size = rsa->pub.size;
+    }
+    qcrypto_nettle_rsa_set_akcipher_size(
+        (QCryptoAkCipher *)rsa, rsa->priv.size);
+    ret = 0;
+
+clear:
+    qcrypto_akcipher_free_rsa_key(rsa_key);
+    return ret;
+}
+
+static int qcrypt_nettle_parse_rsa_public_key(QCryptoNettleRSA *rsa,
+                                              const uint8_t *key,
+                                              size_t keylen)
+{
+    QCryptoAkCipherRSAKey *rsa_key =
+        qcrypto_akcipher_parse_rsa_public_key(key, keylen);
+    int ret = -1;
+    if (!rsa_key) {
+        return ret;
+    }
+    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)) {
+        goto clear;
+    }
+    qcrypto_nettle_rsa_set_akcipher_size(
+        (QCryptoAkCipher *)rsa, rsa->pub.size);
+    ret = 0;
+
+clear:
+    qcrypto_akcipher_free_rsa_key(rsa_key);
+    return ret;
+}
+
+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_driver,
+                                      const void *data, size_t data_len,
+                                      void *enc, size_t enc_len,
+                                      Error **errp)
+{
+
+    QCryptoNettleRSA *rsa =
+        container_of(akcipher_driver, 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 = container_of(akcipher, 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 = container_of(akcipher, 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 clear;
+    }
+
+    if (ret != 1) {
+        error_setg(errp, "Failed to make signature");
+        ret = -1;
+        goto clear;
+    }
+    nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
+    ret = sig_len;
+
+clear:
+    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 = container_of(akcipher, 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 clear;
+    }
+
+    if (ret != 1) {
+        error_setg(errp, "Failed to verify");
+        ret = -1;
+        goto clear;
+    }
+    ret = 0;
+
+clear:
+    mpz_clear(s);
+
+    return ret;
+}
+
+static int qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher,
+                                   Error **errp)
+{
+    qcrypto_nettle_rsa_destroy(akcipher);
+    return 0;
+}
+
+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 err;
+        }
+        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 err;
+        }
+        break;
+
+    default:
+        error_setg(errp, "Unknown akcipher key type %d", type);
+    }
+
+    return (QCryptoAkCipher *)rsa;
+
+err:
+    qcrypto_nettle_rsa_destroy(rsa);
+    return NULL;
+}
+
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+    switch (opts->algorithm) {
+    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 7323a48073..dc29127bb4 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 57f29d6f74..43a90ef211 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..d9cdae9e4b
--- /dev/null
+++ b/crypto/rsakey-builtin.c.inc
@@ -0,0 +1,150 @@
+/*
+ * 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 "rsakey.h"
+#include "asn1_decoder.h"
+
+static int extract_mpi(void *ctx, const uint8_t *value, size_t vlen)
+{
+    QCryptoAkCipherMpi *mpi = (QCryptoAkCipherMpi *)ctx;
+    mpi->data = g_memdup2(value, vlen);
+    mpi->len = vlen;
+    return 0;
+}
+
+static int extract_version(void *ctx, const uint8_t *value, size_t vlen)
+{
+    uint8_t *version = (uint8_t *)ctx;
+    if (vlen != 1) {
+        return -1;
+    }
+    if (*value > 1) {
+        return -1;
+    }
+    *version = *value;
+    return 0;
+}
+
+static int extract_content(void *ctx, const uint8_t *value, size_t vlen)
+{
+    const uint8_t **content = (const uint8_t **)ctx;
+    *content = value;
+    return 0;
+}
+
+/**
+ *
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(const uint8_t *key,
+                                                             size_t keylen)
+{
+    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+    const uint8_t *seq;
+    size_t seq_length;
+    int decode_ret;
+
+    decode_ret = der_decode_seq(&key, &keylen, extract_content, &seq);
+    if (decode_ret <= 0 || keylen != 0) {
+        goto err;
+    }
+    seq_length = decode_ret;
+
+    if (der_decode_int(&seq, &seq_length, extract_mpi, &rsa->n) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->e) <= 0 ||
+        seq_length != 0) {
+        goto err;
+    }
+
+    return rsa;
+
+err:
+    qcrypto_akcipher_free_rsa_key(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
+ *         }
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
+    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;
+
+    decode_ret = der_decode_seq(&key, &keylen, extract_content, &seq);
+    if (decode_ret <= 0 || keylen != 0) {
+        goto err;
+    }
+    seq_length = decode_ret;
+
+    if (der_decode_int(&seq, &seq_length,
+                       extract_version, &version) != sizeof(version) ||
+        version > 1) {
+        goto err;
+    }
+    if (der_decode_int(&seq, &seq_length, extract_mpi, &rsa->n) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->e) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->d) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq) <= 0 ||
+        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u) <= 0) {
+        goto err;
+    }
+    /**
+     * 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 (der_decode_seq(&seq, &seq_length, NULL, NULL) <= 0) {
+            goto err;
+        }
+    }
+    if (seq_length != 0) {
+        goto err;
+    }
+    return rsa;
+
+err:
+    qcrypto_akcipher_free_rsa_key(rsa);
+    return NULL;
+}
diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
new file mode 100644
index 0000000000..3516eba69f
--- /dev/null
+++ b/crypto/rsakey-nettle.c.inc
@@ -0,0 +1,141 @@
+/*
+ * 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
+ *         }
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
+    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 err;
+    }
+
+    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 err;
+        }
+    }
+
+    if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
+        goto err;
+    }
+
+    return rsa;
+
+    /* fallthrough to err */
+err:
+    qcrypto_akcipher_free_rsa_key(rsa);
+    return NULL;
+}
+
+/**
+ *
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(
+    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 err;
+    }
+
+    return rsa;
+
+err:
+    qcrypto_akcipher_free_rsa_key(rsa);
+    return NULL;
+}
diff --git a/crypto/rsakey.c b/crypto/rsakey.c
new file mode 100644
index 0000000000..24704f6b9e
--- /dev/null
+++ b/crypto/rsakey.c
@@ -0,0 +1,43 @@
+/*
+ * 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_free_rsa_key(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);
+}
+
+#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..6345b2222f
--- /dev/null
+++ b/crypto/rsakey.h
@@ -0,0 +1,96 @@
+/*
+ * 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 private key, expected ASN.1 schema:
+ *        RsaPrivKey ::= SEQUENCE {
+ *             version     INTEGER
+ *             n           INTEGER
+ *             e           INTEGER
+ *             d           INTEGER
+ *             p           INTEGER
+ *             q           INTEGER
+ *             dp          INTEGER
+ *             dq          INTEGER
+ *             u           INTEGER
+ *         }
+ *
+ * Return: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
+    const uint8_t *key, size_t keylen);
+
+/**
+ * Parse DER encoded ASN.1 RSA public key, expected ASN.1 schema:
+ *        RsaPubKey ::= SEQUENCE {
+ *             n           INTEGER
+ *             e           INTEGER
+ *         }
+ *
+ * Return: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(
+    const uint8_t *key, size_t keylen);
+
+void qcrypto_akcipher_free_rsa_key(QCryptoAkCipherRSAKey *key);
+
+#endif
diff --git a/meson.build b/meson.build
index aef724ad3c..c1724eb33f 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] 19+ messages in thread

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

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 | 531 +++++++++++++++++++++++++++++++++++
 crypto/akcipher.c            |   4 +-
 2 files changed, 534 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..c109bf0566
--- /dev/null
+++ b/crypto/akcipher-gcrypt.c.inc
@@ -0,0 +1,531 @@
+/*
+ * 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 <stdbool.h>
+
+#include <gcrypt.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "asn1_decoder.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_destroy(QCryptoGcryptRSA *rsa)
+{
+    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->algorithm) {
+    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->algorithm);
+        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)
+{
+    QCryptoAkCipherRSAKey *rsa_key =
+        qcrypto_akcipher_parse_rsa_private_key(key, keylen);
+    gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
+    int ret = -1;
+    bool compute_mul_inv = false;
+    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 clear;
+    }
+
+    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
+                        rsa_key->e.data, rsa_key->e.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    err = gcry_mpi_scan(&d, GCRYMPI_FMT_STD,
+                        rsa_key->d.data, rsa_key->d.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    err = gcry_mpi_scan(&p, GCRYMPI_FMT_STD,
+                        rsa_key->p.data, rsa_key->p.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    err = gcry_mpi_scan(&q, GCRYMPI_FMT_STD,
+                        rsa_key->q.data, rsa_key->q.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    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 clear;
+    }
+    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa,  n);
+    ret = 0;
+
+clear:
+    qcrypto_akcipher_free_rsa_key(rsa_key);
+    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)
+{
+
+    QCryptoAkCipherRSAKey *rsa_key =
+        qcrypto_akcipher_parse_rsa_public_key(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 clear;
+    }
+
+    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
+                        rsa_key->e.data, rsa_key->e.len, NULL);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    err = gcry_sexp_build(&rsa->key, NULL,
+                          "(public-key (rsa (n %m) (e %m)))", n, e);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa, n);
+    ret = 0;
+
+clear:
+    qcrypto_akcipher_free_rsa_key(rsa_key);
+    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 =
+        container_of(akcipher, 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 clear;
+    }
+
+    err = gcry_pk_encrypt(&cipher_sexp, data_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    /* 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 clear;
+    }
+
+    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 clear;
+        }
+        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 clear;
+        }
+
+        /* 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 clear;
+        }
+        memcpy(out, result, actual_len);
+        ret = actual_len;
+    }
+
+clear:
+    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 =
+        container_of(akcipher, 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 clear;
+    }
+
+    err = gcry_pk_decrypt(&data_sexp, cipher_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    /* 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 clear;
+        }
+        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
+                             &actual_len, data_mpi);
+        if (gcry_err_code(err) != 0) {
+            goto clear;
+        }
+         if (actual_len > out_len) {
+            goto clear;
+        }
+        /* 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 clear;
+        }
+        memcpy(out, result, actual_len);
+        ret = actual_len;
+    }
+
+clear:
+    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 =
+        container_of(akcipher, 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) {
+        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 clear;
+    }
+
+    err = gcry_pk_sign(&sig_sexp, dgst_sexp, rsa->key);
+    if (gcry_err_code(err) != 0) {
+        goto clear;
+    }
+
+    /* 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 clear;
+    }
+
+    result = gcry_sexp_nth_data(sig_sexp_item, 1, &actual_len);
+    if (!result || actual_len > out_len) {
+        goto clear;
+     }
+    memcpy(out, result, actual_len);
+    ret = actual_len;
+
+clear:
+    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 =
+        container_of(akcipher, 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 clear;
+    }
+
+    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 clear;
+    }
+
+    err = gcry_pk_verify(sig_sexp, dgst_sexp, rsa->key);
+    if (gcry_err_code(err) == 0) {
+        ret = 0;
+    }
+
+clear:
+    gcry_sexp_release(dgst_sexp);
+    gcry_sexp_release(sig_sexp);
+
+    return ret;
+}
+
+static int qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher,
+                                   Error **errp)
+{
+    qcrypto_gcrypt_rsa_destroy((QCryptoGcryptRSA *)akcipher);
+    return 0;
+}
+
+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 err;
+        }
+        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 err;
+        }
+        break;
+
+    default:
+        error_setg(errp, "Unknown akcipher key type %d", type);
+    }
+
+    return rsa;
+
+err:
+    qcrypto_gcrypt_rsa_destroy(rsa);
+    return NULL;
+}
+
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+    switch (opts->algorithm) {
+    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 dc29127bb4..5c60d54565 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] 19+ messages in thread

* [PATCH v4 7/8] tests/crypto: Add test suite for crypto akcipher
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (5 preceding siblings ...)
  2022-04-11 10:43 ` [PATCH v4 6/8] crypto: Implement RSA algorithm by gcrypt zhenwei pi
@ 2022-04-11 10:43 ` zhenwei pi
  2022-04-26 11:57   ` Daniel P. Berrangé
  2022-04-11 10:43 ` [PATCH v4 8/8] crypto: Introduce RSA algorithm zhenwei pi
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: zhenwei pi @ 2022-04-11 10:43 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck,
	jasowang, 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>
---
 tests/bench/benchmark-crypto-akcipher.c | 161 ++++++
 tests/bench/meson.build                 |   4 +
 tests/bench/test_akcipher_keys.inc      | 537 ++++++++++++++++++
 tests/unit/meson.build                  |   1 +
 tests/unit/test-crypto-akcipher.c       | 708 ++++++++++++++++++++++++
 5 files changed, 1411 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..ebc937d3b8
--- /dev/null
+++ b/tests/bench/benchmark-crypto-akcipher.c
@@ -0,0 +1,161 @@
+/*
+ * 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.algorithm = 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
+
+    QCryptoAkCipher *rsa;
+    uint8_t *dgst, *signature;
+    size_t count;
+
+    rsa = create_rsa_akcipher(priv_key, keylen, PADDING, HASH);
+
+    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());
+
+    g_assert(qcrypto_akcipher_free(rsa, &error_abort) == 0);
+    g_free(dgst);
+    g_free(signature);
+}
+
+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..7adf218135
--- /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 037ad0a0b9..d9cd3b22cb 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..3e6d985d7d
--- /dev/null
+++ b/tests/unit/test-crypto-akcipher.c
@@ -0,0 +1,708 @@
+/*
+ * 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 = {
+            .algorithm = 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 = {
+            .algorithm = 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 = {
+            .algorithm = 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 = {
+            .algorithm = 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);
+    }
+
+    g_assert(qcrypto_akcipher_free(pub_key, &error_abort) == 0);
+    g_assert(qcrypto_akcipher_free(priv_key, &error_abort) == 0);
+}
+
+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] 19+ messages in thread

* [PATCH v4 8/8] crypto: Introduce RSA algorithm
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (6 preceding siblings ...)
  2022-04-11 10:43 ` [PATCH v4 7/8] tests/crypto: Add test suite for crypto akcipher zhenwei pi
@ 2022-04-11 10:43 ` zhenwei pi
  2022-04-12  9:47 ` [PATCH v4 0/8] Introduce akcipher service for virtio-crypto Paolo Bonzini
  2022-04-21  1:41 ` PING: " zhenwei pi
  9 siblings, 0 replies; 19+ messages in thread
From: zhenwei pi @ 2022-04-11 10:43 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck,
	jasowang, 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      | 261 ++++++++++++++++++++----
 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, 594 insertions(+), 144 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 0671bf9f3e..685bf148b2 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.algorithm = 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,10 +382,10 @@ 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)
@@ -288,30 +402,17 @@ static int cryptodev_builtin_sym_close_session(
 }
 
 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 +434,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 +535,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 +557,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..d42599f62f 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,79 @@ 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;
+    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");
+            g_free(key);
+            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,
@@ -193,9 +261,8 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
     Error *local_err = NULL;
 
     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,37 @@ 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) {
+            memset(op_info, 0, sizeof(*op_info));
             g_free(op_info);
         }
-        g_free(req);
     }
+
+    g_free(req);
 }
 
 static void
@@ -397,6 +483,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 +519,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 +661,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 +772,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 +815,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 +844,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 +860,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 +966,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 +1079,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] 19+ messages in thread

* Re: [PATCH v4 0/8] Introduce akcipher service for virtio-crypto
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (7 preceding siblings ...)
  2022-04-11 10:43 ` [PATCH v4 8/8] crypto: Introduce RSA algorithm zhenwei pi
@ 2022-04-12  9:47 ` Paolo Bonzini
  2022-04-12 10:21   ` zhenwei pi
  2022-04-21  1:41 ` PING: " zhenwei pi
  9 siblings, 1 reply; 19+ messages in thread
From: Paolo Bonzini @ 2022-04-12  9:47 UTC (permalink / raw)
  To: zhenwei pi, mst, berrange, arei.gonglei, Simo Sorce
  Cc: helei.sig11, jasowang, cohuck, qemu-devel, virtualization, linux-crypto


> In our plan, the feature is designed for HTTPS offloading case and
> other applications which use kernel RSA/ecdsa by keyctl syscall.

Hi Zhenwei,

what is the % of time spent doing asymmetric key operations in your
benchmark?  I am not very familiar with crypto acceleration but my
understanding has always been that most time is spent doing either
hashing (for signing) or symmetric key operations (for encryption).

If I understand correctly, without support for acceleration these 
patches are more of a demonstration of virtio-crypto, or usable for 
testing purposes.

Would it be possible to extend virtio-crypto to use keys already in the
host keyctl, or in a PKCS#11 smartcard, so that virtio-crypto could also
provide the functionality of an HSM?  Or does the standard require that
the keys are provided by the guest?

Paolo

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

* Re: Re: [PATCH v4 0/8] Introduce akcipher service for virtio-crypto
  2022-04-12  9:47 ` [PATCH v4 0/8] Introduce akcipher service for virtio-crypto Paolo Bonzini
@ 2022-04-12 10:21   ` zhenwei pi
  0 siblings, 0 replies; 19+ messages in thread
From: zhenwei pi @ 2022-04-12 10:21 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: helei.sig11, jasowang, cohuck, qemu-devel, arei.gonglei,
	Simo Sorce, virtualization, linux-crypto, mst, berrange



On 4/12/22 17:47, Paolo Bonzini wrote:
> 
>> In our plan, the feature is designed for HTTPS offloading case and
>> other applications which use kernel RSA/ecdsa by keyctl syscall.
> 
> Hi Zhenwei,
> 
> what is the % of time spent doing asymmetric key operations in your
> benchmark?  I am not very familiar with crypto acceleration but my
> understanding has always been that most time is spent doing either
> hashing (for signing) or symmetric key operations (for encryption).
> 
> If I understand correctly, without support for acceleration these 
> patches are more of a demonstration of virtio-crypto, or usable for 
> testing purposes.
> 

Hi, Paolo

This is the perf result of nginx+openssl CPU calculation, the heavy load 
from openssl uses the most time(as same as you mentioned).
27.37%    26.00%  nginx            libcrypto.so.1.1          [.] 
__bn_sqrx8x_reduction
20.58%    19.52%  nginx            libcrypto.so.1.1          [.] 
mulx4x_internal
16.73%    15.89%  nginx            libcrypto.so.1.1          [.] 
bn_sqrx8x_internal
  8.79%     0.00%  nginx            [unknown]                 [k] 
0000000000000000
  7.26%     0.00%  nginx            [unknown]                 [.] 
0x89388669992a0cbc
  7.00%     0.00%  nginx            [unknown]                 [k] 
0x45f0e480d5f2a58e
  6.76%     0.02%  nginx            [kernel.kallsyms]         [k] 
entry_SYSCALL_64_after_hwframe
  6.74%     0.02%  nginx            [kernel.kallsyms]         [k] 
do_syscall_64
  6.61%     0.00%  nginx            [unknown]                 [.] 
0xa75a60d7820f9ffb
  6.47%     0.00%  nginx            [unknown]                 [k] 
0xe91223f6da36254c
  5.51%     0.01%  nginx            [kernel.kallsyms]         [k] 
asm_common_interrupt
  5.46%     0.01%  nginx            [kernel.kallsyms]         [k] 
common_interrupt
  5.16%     0.04%  nginx            [kernel.kallsyms]         [k] 
__softirqentry_text_start
  4.92%     0.01%  nginx            [kernel.kallsyms]         [k] 
irq_exit_rcu
  4.91%     0.04%  nginx            [kernel.kallsyms]         [k] 
net_rx_action


This is the result of nginx+openssl keyctl offload(virtio crypto + host 
keyctl + Intel QAT):
30.38%     0.08%  nginx            [kernel.kallsyms]         [k] 
entry_SYSCALL_64_after_hwframe
30.29%     0.07%  nginx            [kernel.kallsyms]         [k] 
do_syscall_64
23.84%     0.00%  nginx            [unknown]                 [k] 
0000000000000000
14.24%     0.03%  nginx            [kernel.kallsyms]         [k] 
asm_common_interrupt
14.06%     0.05%  nginx            [kernel.kallsyms]         [k] 
common_interrupt
12.99%     0.11%  nginx            [kernel.kallsyms]         [k] 
__softirqentry_text_start
12.27%     0.12%  nginx            [kernel.kallsyms]         [k] 
net_rx_action
12.13%     0.03%  nginx            [kernel.kallsyms]         [k] __napi_poll
12.06%     0.06%  nginx            [kernel.kallsyms]         [k] 
irq_exit_rcu
10.49%     0.14%  nginx            libssl.so.1.1             [.] 
tls_process_client_key_exchange
10.21%     0.12%  nginx            [virtio_net]              [k] 
virtnet_poll
10.13%     0.04%  nginx            libc-2.28.so              [.] syscall
10.12%     0.03%  nginx            kctl-engine.so            [.] 
kctl_rsa_priv_dec
10.02%     0.02%  nginx            kctl-engine.so            [.] 
kctl_hw_rsa_priv_func
  9.98%     0.01%  nginx            libkeyutils.so.1.10       [.] 
keyctl_pkey_decrypt
  9.95%     0.02%  nginx            libkeyutils.so.1.10       [.] keyctl
  9.77%     0.03%  nginx            [kernel.kallsyms]         [k] 
keyctl_pkey_e_d_s
  8.97%     0.00%  nginx            [unknown]                 [k] 
0x00007f4adbb81f0b
  8.78%     0.08%  nginx            libpthread-2.28.so        [.] 
__libc_write
  8.49%     0.05%  nginx            [kernel.kallsyms]         [k] 
netif_receive_skb_list_internal

The RSA part gets reduced, and the QPS of https improves to ~200%.

Something may be ignored in this cover letter:
[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.
-> Yes, this patch is a demonstration of virtio-crypto.

[5] keyctl backend is in development, we will post this feature in 
Q2-2022. keyctl backend can use hardware acceleration(Ex, Intel QAT).
-> This is our plan. Currently it's still in developing.


> Would it be possible to extend virtio-crypto to use keys already in the
> host keyctl, or in a PKCS#11 smartcard, so that virtio-crypto could also
> provide the functionality of an HSM?  Or does the standard require that
> the keys are provided by the guest?
> 
> Paolo

I'm very interested in this, I'll try in Q3-2022 or later.

-- 
zhenwei pi

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

* PING: [PATCH v4 0/8] Introduce akcipher service for virtio-crypto
  2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
                   ` (8 preceding siblings ...)
  2022-04-12  9:47 ` [PATCH v4 0/8] Introduce akcipher service for virtio-crypto Paolo Bonzini
@ 2022-04-21  1:41 ` zhenwei pi
  2022-04-21  8:10   ` Daniel P. Berrangé
  9 siblings, 1 reply; 19+ messages in thread
From: zhenwei pi @ 2022-04-21  1:41 UTC (permalink / raw)
  To: mst, berrange, arei.gonglei
  Cc: qemu-devel, virtualization, linux-crypto, helei.sig11, cohuck, jasowang

Hi Daniel,
Could you please review this series?


On 4/11/22 18:43, zhenwei pi wrote:
> 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 (4):
>    crypto-akcipher: Introduce akcipher types to qapi
>    crypto: add ASN.1 decoder
>    crypto: Implement RSA algorithm by hogweed
>    crypto: Implement RSA algorithm by gcrypt
> 
> Zhenwei Pi (3):
>    virtio-crypto: header update
>    crypto: Introduce akcipher crypto class
>    crypto: Introduce RSA algorithm
> 
> lei he (1):
>    tests/crypto: Add test suite for crypto akcipher
> 
>   backends/cryptodev-builtin.c                  | 261 ++++++-
>   backends/cryptodev-vhost-user.c               |  34 +-
>   backends/cryptodev.c                          |  32 +-
>   crypto/akcipher-gcrypt.c.inc                  | 531 +++++++++++++
>   crypto/akcipher-nettle.c.inc                  | 448 +++++++++++
>   crypto/akcipher.c                             | 108 +++
>   crypto/akcipherpriv.h                         |  43 ++
>   crypto/asn1_decoder.c                         | 161 ++++
>   crypto/asn1_decoder.h                         |  75 ++
>   crypto/meson.build                            |   6 +
>   crypto/rsakey-builtin.c.inc                   | 150 ++++
>   crypto/rsakey-nettle.c.inc                    | 141 ++++
>   crypto/rsakey.c                               |  43 ++
>   crypto/rsakey.h                               |  96 +++
>   hw/virtio/virtio-crypto.c                     | 323 ++++++--
>   include/crypto/akcipher.h                     | 151 ++++
>   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       | 161 ++++
>   tests/bench/meson.build                       |   4 +
>   tests/bench/test_akcipher_keys.inc            | 537 +++++++++++++
>   tests/unit/meson.build                        |   2 +
>   tests/unit/test-crypto-akcipher.c             | 708 ++++++++++++++++++
>   tests/unit/test-crypto-asn1-decoder.c         | 289 +++++++
>   27 files changed, 4404 insertions(+), 145 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/asn1_decoder.c
>   create mode 100644 crypto/asn1_decoder.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-asn1-decoder.c
> 

-- 
zhenwei pi

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

* Re: PING: [PATCH v4 0/8] Introduce akcipher service for virtio-crypto
  2022-04-21  1:41 ` PING: " zhenwei pi
@ 2022-04-21  8:10   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-21  8:10 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Thu, Apr 21, 2022 at 09:41:40AM +0800, zhenwei pi wrote:
> Hi Daniel,
> Could you please review this series?

Yes, its on my to do. I've been on holiday for 2 weeks, so still catching
up on the backlog of reviews.

> On 4/11/22 18:43, zhenwei pi wrote:
> > 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 (4):
> >    crypto-akcipher: Introduce akcipher types to qapi
> >    crypto: add ASN.1 decoder
> >    crypto: Implement RSA algorithm by hogweed
> >    crypto: Implement RSA algorithm by gcrypt
> > 
> > Zhenwei Pi (3):
> >    virtio-crypto: header update
> >    crypto: Introduce akcipher crypto class
> >    crypto: Introduce RSA algorithm
> > 
> > lei he (1):
> >    tests/crypto: Add test suite for crypto akcipher
> > 
> >   backends/cryptodev-builtin.c                  | 261 ++++++-
> >   backends/cryptodev-vhost-user.c               |  34 +-
> >   backends/cryptodev.c                          |  32 +-
> >   crypto/akcipher-gcrypt.c.inc                  | 531 +++++++++++++
> >   crypto/akcipher-nettle.c.inc                  | 448 +++++++++++
> >   crypto/akcipher.c                             | 108 +++
> >   crypto/akcipherpriv.h                         |  43 ++
> >   crypto/asn1_decoder.c                         | 161 ++++
> >   crypto/asn1_decoder.h                         |  75 ++
> >   crypto/meson.build                            |   6 +
> >   crypto/rsakey-builtin.c.inc                   | 150 ++++
> >   crypto/rsakey-nettle.c.inc                    | 141 ++++
> >   crypto/rsakey.c                               |  43 ++
> >   crypto/rsakey.h                               |  96 +++
> >   hw/virtio/virtio-crypto.c                     | 323 ++++++--
> >   include/crypto/akcipher.h                     | 151 ++++
> >   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       | 161 ++++
> >   tests/bench/meson.build                       |   4 +
> >   tests/bench/test_akcipher_keys.inc            | 537 +++++++++++++
> >   tests/unit/meson.build                        |   2 +
> >   tests/unit/test-crypto-akcipher.c             | 708 ++++++++++++++++++
> >   tests/unit/test-crypto-asn1-decoder.c         | 289 +++++++
> >   27 files changed, 4404 insertions(+), 145 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/asn1_decoder.c
> >   create mode 100644 crypto/asn1_decoder.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-asn1-decoder.c
> > 
> 
> -- 
> zhenwei pi
> 

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] 19+ messages in thread

* Re: [PATCH v4 2/8] crypto-akcipher: Introduce akcipher types to qapi
  2022-04-11 10:43 ` [PATCH v4 2/8] crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
@ 2022-04-26 10:34   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 10:34 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:21PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Introduce akcipher types, also include RSA related types.
> 
> 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(+)

snip

> +##
> +# @QCryptoAkCipherOptions:
> +#
> +# The options that are available for all asymmetric key algorithms
> +# when creating a new QCryptoAkCipher.
> +#
> +# Since: 7.1
> +##
> +{ 'union': 'QCryptoAkCipherOptions',
> +  'base': { 'algorithm': 'QCryptoAkCipherAlgorithm' },
> +  'discriminator': 'algorithm',
> +  'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}

I mistakenly suggested 'algorithm' here, but for consistency
with other fields, I should have said just 'alg'.

With that change

  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] 19+ messages in thread

* Re: [PATCH v4 3/8] crypto: Introduce akcipher crypto class
  2022-04-11 10:43 ` [PATCH v4 3/8] crypto: Introduce akcipher crypto class zhenwei pi
@ 2022-04-26 10:41   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 10:41 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:22PM +0800, zhenwei pi wrote:
> Support basic asymmetric operations: encrypt, decrypt, sign and
> verify.
> 
> 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     |  43 +++++++++++
>  crypto/meson.build        |   1 +
>  include/crypto/akcipher.h | 151 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 297 insertions(+)
>  create mode 100644 crypto/akcipher.c
>  create mode 100644 crypto/akcipherpriv.h
>  create mode 100644 include/crypto/akcipher.h


> diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h
> new file mode 100644
> index 0000000000..da9e54a796
> --- /dev/null
> +++ b/crypto/akcipherpriv.h
> @@ -0,0 +1,43 @@
> +/*
> + * 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"
> +
> +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);
> +    int (*free)(QCryptoAkCipher *akcipher, Error **errp);
> +};
> +
> +#endif /* QCRYPTO_AKCIPHER_H */


> diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
> new file mode 100644
> index 0000000000..c1970b3b3b
> --- /dev/null
> +++ b/include/crypto/akcipher.h
> @@ -0,0 +1,151 @@
> +/*
> + * 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;

This belongs here.

> +typedef struct QCryptoAkCipherDriver QCryptoAkCipherDriver;

This and...

> +
> +struct QCryptoAkCipher {
> +    QCryptoAkCipherAlgorithm alg;
> +    QCryptoAkCipherKeyType type;
> +    int max_plaintext_len;
> +    int max_ciphertext_len;
> +    int max_signature_len;
> +    int max_dgst_len;
> +    QCryptoAkCipherDriver *driver;
> +};

...this should be in the akcipherpriv.h file though, since
they're only for internal usage.



> +/**
> + * qcrypto_akcipher_encrypt:
> + * @akcipher: akcipher context
> + * @in: plaintext pending to be encrypted
> + * @in_len: length of the plaintext, MUST less or equal to max_plaintext_len
> + * @out: buffer to store the ciphertext
> + * @out_len: the length of ciphertext buffer, usually equals to
> + *           max_ciphertext_len
> + * @errp: error pointer
> + *
> + * Encrypt data 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
> + * @out: buffer to store the plaintext
> + * @out_len: length of the plaintext buffer, usually less or equals to
> + *           max_plaintext_len

This field should be private, so we need to point people to the
methods instead. Rather than making this line so long...

> + * @errp: error pointer
> + *
> + * Decrypt ciphertext and write plaintext into out

...put here

  "@out_len should be less or equal to the size reported
   by a call to qcrypto_akcipher_max_plaintext_len()'

The same for other places where you mention limits.

> + *
> + * 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
> + * @out: buffer to store the signature
> + * @out_len: length of the signature buffer, usually equals to max_signature_len
> + * @errp: error pointer
> + *
> + * Generate signature for data using akcipher
> + *
> + * 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 used to do verifycation
> + * @in: pointer to the signature
> + * @in_len: length of the signature
> + * @in2: pointer to original data
> + * @in2_len: the length of original data
> + * @errp: error pointer
> + *
> + * Verify the signature and the data 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);
> +
> +int qcrypto_akcipher_free(QCryptoAkCipher *akcipher, Error **errp);

Add in

G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipher, qcrypto_akcipher_free)


This allows users to do

   g_autoptr(QCryptoAkCIpher) cipher = qcrypto_akcipher_new(...)


and get automatic free'ing when exiting the scope.

> +
> +
> +#endif /* QCRYPTO_AKCIPHER_H */
> -- 
> 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] 19+ messages in thread

* Re: [PATCH v4 4/8] crypto: add ASN.1 decoder
  2022-04-11 10:43 ` [PATCH v4 4/8] crypto: add ASN.1 decoder zhenwei pi
@ 2022-04-26 11:38   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 11:38 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:23PM +0800, zhenwei pi wrote:
> From: Lei He <helei.sig11@bytedance.com>
> 
> Add an ANS.1 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/asn1_decoder.c                 | 161 ++++++++++++++
>  crypto/asn1_decoder.h                 |  75 +++++++
>  crypto/meson.build                    |   1 +
>  tests/unit/meson.build                |   1 +
>  tests/unit/test-crypto-asn1-decoder.c | 289 ++++++++++++++++++++++++++
>  5 files changed, 527 insertions(+)
>  create mode 100644 crypto/asn1_decoder.c
>  create mode 100644 crypto/asn1_decoder.h
>  create mode 100644 tests/unit/test-crypto-asn1-decoder.c
> 
> diff --git a/crypto/asn1_decoder.c b/crypto/asn1_decoder.c
> new file mode 100644
> index 0000000000..506487f713
> --- /dev/null
> +++ b/crypto/asn1_decoder.c

Lets rename this to simply 'der.c' since the DER format is just one
way ASN1 can be encoded, and we only care about DER.

> @@ -0,0 +1,161 @@
> +/*
> + * QEMU Crypto ASN.1 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/asn1_decoder.h"
> +
> +enum der_type_tag {

QCryptoDERTypeTag

> +    der_type_tag_bool = 0x1,

QCRYPTO_DER_TYPE_TAG_XXXX  for each constant

> +    der_type_tag_int = 0x2,
> +    der_type_tag_bit_str = 0x3,
> +    der_type_tag_oct_str = 0x4,
> +    der_type_tag_oct_null = 0x5,
> +    der_type_tag_oct_oid = 0x6,
> +    der_type_tag_seq = 0x10,
> +    der_type_tag_set = 0x11,
> +};
> +
> +#define DER_CONSTRUCTED_MASK 0x20
> +#define DER_SHORT_LEN_MASK 0x80

QCRYPTO_DER_ as the name prefix for constants.

> +
> +static uint8_t der_peek_byte(const uint8_t **data, size_t *dlen)

'qcrypto_der_' as the name prefix for all methods


> +{
> +    return **data;
> +}
> +
> +static void der_cut_nbytes(const uint8_t **data, size_t *dlen,
> +                           size_t nbytes)
> +{
> +    *data += nbytes;
> +    *dlen -= nbytes;
> +}
> +
> +static uint8_t der_cut_byte(const uint8_t **data, size_t *dlen)
> +{
> +    uint8_t val = der_peek_byte(data, dlen);
> +
> +    der_cut_nbytes(data, dlen, 1);
> +
> +    return val;
> +}
> +
> +static int der_invoke_callback(DERDecodeCb cb, void *ctx,
> +                           const uint8_t *value, size_t vlen)

Make sure the 'const uint8_t' is vertically aligned with
the 'DERDecodeCb'

> +{
> +    if (!cb) {
> +        return 0;
> +    }
> +
> +    return cb(ctx, value, vlen);
> +}
> +
> +static int der_extract_definite_data(const uint8_t **data, size_t *dlen,
> +                                     DERDecodeCb cb, void *ctx)
> +{
> +    const uint8_t *value;
> +    size_t vlen = 0;
> +    uint8_t byte_count = der_cut_byte(data, dlen);
> +
> +    /* short format of definite-length */
> +    if (!(byte_count & DER_SHORT_LEN_MASK)) {
> +        if (byte_count > *dlen) {
> +            return -1;
> +        }
> +
> +        value = *data;
> +        vlen = byte_count;
> +        der_cut_nbytes(data, dlen, vlen);
> +
> +        if (der_invoke_callback(cb, ctx, value, vlen)) {
> +            return -1;
> +        }
> +        return vlen;
> +    }
> +
> +    /* Ignore highest bit */
> +    byte_count &= ~DER_SHORT_LEN_MASK;
> +
> +    /*
> +     * size_t is enough to express the length, although the der encoding
> +     * standard supports larger length.
> +     */
> +    if (byte_count > sizeof(size_t)) {
> +        return -1;
> +    }
> +
> +    while (byte_count--) {
> +        vlen <<= 8;
> +        vlen += der_cut_byte(data, dlen);
> +    }
> +
> +    if (vlen > *dlen) {
> +        return -1;
> +    }
> +
> +    value = *data;
> +    der_cut_nbytes(data, dlen, vlen);
> +
> +    if (der_invoke_callback(cb, ctx, value, vlen) != 0) {
> +        return -1;
> +    }
> +    return vlen;
> +}
> +
> +static int der_extract_data(const uint8_t **data, size_t *dlen,
> +                            DERDecodeCb cb, void *ctx)
> +{
> +    uint8_t val = der_peek_byte(data, dlen);
> +
> +    /* must use definite length format */
> +    if (val == DER_SHORT_LEN_MASK) {
> +        return -1;
> +    }
> +
> +    return der_extract_definite_data(data, dlen, cb, ctx);
> +}
> +
> +int der_decode_int(const uint8_t **data, size_t *dlen,
> +                   DERDecodeCb cb, void *ctx)
> +{
> +    uint8_t tag = der_cut_byte(data, dlen);
> +
> +    /* INTEGER must encoded in primitive-form */
> +    if (tag != der_type_tag_int) {
> +        return -1;
> +    }
> +
> +    return der_extract_data(data, dlen, cb, ctx);
> +}
> +
> +int der_decode_seq(const uint8_t **data, size_t *dlen,
> +                   DERDecodeCb cb, void *ctx)
> +{
> +    uint8_t val = der_cut_byte(data, dlen);
> +
> +    /* SEQUENCE must use constructed form */
> +    if (val != (der_type_tag_seq | DER_CONSTRUCTED_MASK)) {
> +        return -1;
> +    }
> +
> +    return der_extract_data(data, dlen, cb, ctx);
> +}
> diff --git a/crypto/asn1_decoder.h b/crypto/asn1_decoder.h
> new file mode 100644
> index 0000000000..e414807af2
> --- /dev/null
> +++ b/crypto/asn1_decoder.h
> @@ -0,0 +1,75 @@
> +/*
> + * 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
> +
> +/* Simple decoder used to parse DER encoded rsa keys. */
> +
> +/**
> + *  @opaque: user content.
> + *  @value: the starting address of |value| part of 'Tag-Length-Value' pattern.
> + *  @vlen: length of the |value|.
> + */
> +typedef int (*DERDecodeCb) (void *opaque, const uint8_t *value, size_t vlen);

Essentially we want qcrypto_der / QCryptoDer / QCRYPTO_DER as a prefix
for everything as there is too great a chance of 'der' alone clashing
with some other crypto library we're using.

Can you document the return value. If if can return errors, then I
think we need an 'Error **errp' here.

> +
> +/**
> + * 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 der_decode_int(const uint8_t **data,
> +                   size_t *dlen,
> +                   DERDecodeCb cb,
> +                   void *opaque);

Since we're returning errors, I think we should have an 'Error **errp'
in this method, and the next, so we can report useful error messages
to the caller.

> +
> +/**
> + * 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 der_decode_seq(const uint8_t **data,
> +                   size_t *dlen,
> +                   DERDecodeCb cb,
> +                   void *opaque);
> +
> +#endif  /* QCRYPTO_ASN1_DECODER_H */
> diff --git a/crypto/meson.build b/crypto/meson.build
> index 7647d5e243..57f29d6f74 100644
> --- a/crypto/meson.build
> +++ b/crypto/meson.build
> @@ -2,6 +2,7 @@ crypto_ss.add(genh)
>  crypto_ss.add(files(
>    'afsplit.c',
>    'akcipher.c',
> +  'asn1_decoder.c',
>    'block-luks.c',
>    'block-qcow.c',
>    'block.c',
> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
> index 96b295263e..037ad0a0b9 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-asn1-decoder':[crypto],
>  }
>  
>  if have_system or have_tools
> diff --git a/tests/unit/test-crypto-asn1-decoder.c b/tests/unit/test-crypto-asn1-decoder.c
> new file mode 100644
> index 0000000000..16e10b12f9
> --- /dev/null
> +++ b/tests/unit/test-crypto-asn1-decoder.c
> @@ -0,0 +1,289 @@
> +/*
> + * 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/asn1_decoder.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);
> +    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)
> +{
> +    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)
> +            == checker->exp_vlen);
> +        if (checker->action == 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/asn1-decoder/parse-rsa512-priv-key",
> +    .test_data = test_rsa512_priv_key,
> +    .test_data_len = sizeof(test_rsa512_priv_key) - 1,
> +    .checker = {
> +        { der_decode_seq, checker_callback,
> +          test_rsa512_priv_key + 4, 313 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 4 + 2, 1 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 7 + 2, 65 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 74 + 2, 3 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 79 + 2, 64 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 145 + 2, 33 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 180 + 2, 33 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 215 + 2, 32 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 249 + 2, 32 },
> +        { der_decode_int, checker_callback,
> +          test_rsa512_priv_key + 283 + 2, 32 },
> +    },
> +},
> +{
> +    .path = "/crypto/asn1-decoder/parse-rsa2048-priv-key",
> +    .test_data = test_rsa2048_priv_key,
> +    .test_data_len = sizeof(test_rsa2048_priv_key) - 1,
> +    .checker = {
> +        { der_decode_seq, checker_callback,
> +          test_rsa2048_priv_key + 4, 1190 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 4 + 2, 1 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 7 + 4, 257 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 268 + 2, 3 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 273 + 4, 257 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 534 + 3, 129 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 666 + 3, 129 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 798 + 3, 129 },
> +        { der_decode_int, checker_callback,
> +          test_rsa2048_priv_key + 930 + 3, 129 },
> +        { 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
> 

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] 19+ messages in thread

* Re: [PATCH v4 5/8] crypto: Implement RSA algorithm by hogweed
  2022-04-11 10:43 ` [PATCH v4 5/8] crypto: Implement RSA algorithm by hogweed zhenwei pi
@ 2022-04-26 11:51   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 11:51 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:24PM +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 | 448 +++++++++++++++++++++++++++++++++++
>  crypto/akcipher.c            |   4 +
>  crypto/meson.build           |   4 +
>  crypto/rsakey-builtin.c.inc  | 150 ++++++++++++
>  crypto/rsakey-nettle.c.inc   | 141 +++++++++++
>  crypto/rsakey.c              |  43 ++++
>  crypto/rsakey.h              |  96 ++++++++
>  meson.build                  |  11 +
>  8 files changed, 897 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..de163cd89e
> --- /dev/null
> +++ b/crypto/akcipher-nettle.c.inc
> @@ -0,0 +1,448 @@
> +/*
> + * 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 <stdbool.h>
> +
> +#include <nettle/rsa.h>
> +
> +#include "qemu/osdep.h"
> +#include "qemu/host-utils.h"
> +#include "asn1_decoder.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_destroy(void *ptr)
> +{
> +    QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)ptr;
> +    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->algorithm) {
> +    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->algorithm);
> +        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)
> +{
> +    QCryptoAkCipherRSAKey *rsa_key =
> +        qcrypto_akcipher_parse_rsa_private_key(key, keylen);
> +    int ret = -1;
> +    if (!rsa_key) {
> +        return ret;
> +    }
> +
> +    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)) {
> +        goto clear;
> +    }
> +
> +    /**
> +     * 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)) {
> +            goto clear;
> +        }
> +    } else {
> +        rsa->priv.size = rsa->pub.size;
> +    }
> +    qcrypto_nettle_rsa_set_akcipher_size(
> +        (QCryptoAkCipher *)rsa, rsa->priv.size);
> +    ret = 0;
> +
> +clear:
> +    qcrypto_akcipher_free_rsa_key(rsa_key);
> +    return ret;
> +}
> +
> +static int qcrypt_nettle_parse_rsa_public_key(QCryptoNettleRSA *rsa,
> +                                              const uint8_t *key,
> +                                              size_t keylen)
> +{
> +    QCryptoAkCipherRSAKey *rsa_key =
> +        qcrypto_akcipher_parse_rsa_public_key(key, keylen);
> +    int ret = -1;
> +    if (!rsa_key) {
> +        return ret;
> +    }
> +    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)) {
> +        goto clear;
> +    }
> +    qcrypto_nettle_rsa_set_akcipher_size(
> +        (QCryptoAkCipher *)rsa, rsa->pub.size);
> +    ret = 0;
> +
> +clear:
> +    qcrypto_akcipher_free_rsa_key(rsa_key);
> +    return ret;
> +}
> +
> +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_driver,
> +                                      const void *data, size_t data_len,
> +                                      void *enc, size_t enc_len,
> +                                      Error **errp)
> +{
> +
> +    QCryptoNettleRSA *rsa =
> +        container_of(akcipher_driver, 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 = container_of(akcipher, 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 = container_of(akcipher, 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 clear;
> +    }
> +
> +    if (ret != 1) {
> +        error_setg(errp, "Failed to make signature");
> +        ret = -1;
> +        goto clear;
> +    }
> +    nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
> +    ret = sig_len;
> +
> +clear:

Could you call this 'cleanup' since that's a naming convention
I try to use in crypto code. Basically the two common naming
conventions are:

 'cleanup:' - if both success & failure paths go through it
 'error:'   - if only the failure path goes through it


> +    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 = container_of(akcipher, 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 clear;
> +    }
> +
> +    if (ret != 1) {
> +        error_setg(errp, "Failed to verify");
> +        ret = -1;
> +        goto clear;
> +    }
> +    ret = 0;
> +
> +clear:
> +    mpz_clear(s);
> +
> +    return ret;
> +}
> +
> +static int qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher,
> +                                   Error **errp)
> +{
> +    qcrypto_nettle_rsa_destroy(akcipher);
> +    return 0;
> +}
> +
> +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 err;
> +        }
> +        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 err;
> +        }
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown akcipher key type %d", type);

Here we need 'goto err'

> +    }
> +
> +    return (QCryptoAkCipher *)rsa;
> +
> +err:

Lets call the label 'error' since that's the more common convention
in crypto code.

> +    qcrypto_nettle_rsa_destroy(rsa);
> +    return NULL;
> +}
> +
> +
> +bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
> +{
> +    switch (opts->algorithm) {
> +    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 7323a48073..dc29127bb4 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 57f29d6f74..43a90ef211 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..d9cdae9e4b
> --- /dev/null
> +++ b/crypto/rsakey-builtin.c.inc
> @@ -0,0 +1,150 @@
> +/*
> + * 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 "rsakey.h"
> +#include "asn1_decoder.h"
> +
> +static int extract_mpi(void *ctx, const uint8_t *value, size_t vlen)
> +{
> +    QCryptoAkCipherMpi *mpi = (QCryptoAkCipherMpi *)ctx;
> +    mpi->data = g_memdup2(value, vlen);
> +    mpi->len = vlen;
> +    return 0;
> +}
> +
> +static int extract_version(void *ctx, const uint8_t *value, size_t vlen)
> +{
> +    uint8_t *version = (uint8_t *)ctx;
> +    if (vlen != 1) {
> +        return -1;
> +    }
> +    if (*value > 1) {
> +        return -1;
> +    }
> +    *version = *value;
> +    return 0;
> +}
> +
> +static int extract_content(void *ctx, const uint8_t *value, size_t vlen)
> +{
> +    const uint8_t **content = (const uint8_t **)ctx;
> +    *content = value;
> +    return 0;
> +}
> +
> +/**
> + *
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(const uint8_t *key,
> +                                                             size_t keylen)
> +{
> +    QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
> +    const uint8_t *seq;
> +    size_t seq_length;
> +    int decode_ret;
> +
> +    decode_ret = der_decode_seq(&key, &keylen, extract_content, &seq);
> +    if (decode_ret <= 0 || keylen != 0) {
> +        goto err;
> +    }
> +    seq_length = decode_ret;
> +
> +    if (der_decode_int(&seq, &seq_length, extract_mpi, &rsa->n) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->e) <= 0 ||
> +        seq_length != 0) {
> +        goto err;
> +    }
> +
> +    return rsa;
> +
> +err:
> +    qcrypto_akcipher_free_rsa_key(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
> + *         }
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
> +    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;
> +
> +    decode_ret = der_decode_seq(&key, &keylen, extract_content, &seq);
> +    if (decode_ret <= 0 || keylen != 0) {
> +        goto err;
> +    }
> +    seq_length = decode_ret;
> +
> +    if (der_decode_int(&seq, &seq_length,
> +                       extract_version, &version) != sizeof(version) ||
> +        version > 1) {
> +        goto err;
> +    }
> +    if (der_decode_int(&seq, &seq_length, extract_mpi, &rsa->n) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->e) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->d) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq) <= 0 ||
> +        der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u) <= 0) {
> +        goto err;
> +    }
> +    /**
> +     * 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 (der_decode_seq(&seq, &seq_length, NULL, NULL) <= 0) {
> +            goto err;
> +        }
> +    }
> +    if (seq_length != 0) {
> +        goto err;
> +    }
> +    return rsa;
> +
> +err:
> +    qcrypto_akcipher_free_rsa_key(rsa);
> +    return NULL;
> +}
> diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
> new file mode 100644
> index 0000000000..3516eba69f
> --- /dev/null
> +++ b/crypto/rsakey-nettle.c.inc
> @@ -0,0 +1,141 @@
> +/*
> + * 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>
 
No need for stdbool.h, provided you include qemu/osdep.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
> + *         }
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
> +    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 err;
> +    }

The common QEMU code style is to have the '||' at the end of the previous
line, rather than start of the following line.


> +
> +    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 err;
> +        }
> +    }
> +
> +    if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
> +        goto err;
> +    }
> +
> +    return rsa;
> +
> +    /* fallthrough to err */
> +err:
> +    qcrypto_akcipher_free_rsa_key(rsa);
> +    return NULL;
> +}
> +
> +/**
> + *
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(
> +    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 err;
> +    }
> +
> +    return rsa;
> +
> +err:
> +    qcrypto_akcipher_free_rsa_key(rsa);
> +    return NULL;
> +}
> diff --git a/crypto/rsakey.c b/crypto/rsakey.c
> new file mode 100644
> index 0000000000..24704f6b9e
> --- /dev/null
> +++ b/crypto/rsakey.c
> @@ -0,0 +1,43 @@
> +/*
> + * 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_free_rsa_key(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);
> +}
> +
> +#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..6345b2222f
> --- /dev/null
> +++ b/crypto/rsakey.h
> @@ -0,0 +1,96 @@
> +/*
> + * 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;

For an abbreviation like Mpi, we should use capitals eg

  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 private key, expected ASN.1 schema:
> + *        RsaPrivKey ::= SEQUENCE {
> + *             version     INTEGER
> + *             n           INTEGER
> + *             e           INTEGER
> + *             d           INTEGER
> + *             p           INTEGER
> + *             q           INTEGER
> + *             dp          INTEGER
> + *             dq          INTEGER
> + *             u           INTEGER
> + *         }
> + *
> + * Return: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_private_key(
> +    const uint8_t *key, size_t keylen);
> +/**
> + * Parse DER encoded ASN.1 RSA public key, expected ASN.1 schema:
> + *        RsaPubKey ::= SEQUENCE {
> + *             n           INTEGER
> + *             e           INTEGER
> + *         }
> + *
> + * Return: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
> + */
> +QCryptoAkCipherRSAKey *qcrypto_akcipher_parse_rsa_public_key(
> +    const uint8_t *key, size_t keylen);

We could just have one method using QCryptoAkCipherKeyType as a param eg

     qcrypto_akcipher_rsakey_parse(QCryptoAkCipherKeyType type,
                                   const uint8_t *key, size_t keylen);

> +void qcrypto_akcipher_free_rsa_key(QCryptoAkCipherRSAKey *key);

Could you call this one  qcrypto_akcipher_rsakey_free 

Add add g_autoptr support using:

  G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey, qcrypto_akcipher_rsakey_free)


> +
> +#endif

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] 19+ messages in thread

* Re: [PATCH v4 6/8] crypto: Implement RSA algorithm by gcrypt
  2022-04-11 10:43 ` [PATCH v4 6/8] crypto: Implement RSA algorithm by gcrypt zhenwei pi
@ 2022-04-26 11:54   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 11:54 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:25PM +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 | 531 +++++++++++++++++++++++++++++++++++
>  crypto/akcipher.c            |   4 +-
>  2 files changed, 534 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..c109bf0566
> --- /dev/null
> +++ b/crypto/akcipher-gcrypt.c.inc
> @@ -0,0 +1,531 @@
> +/*
> + * 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 <stdbool.h>
> +
> +#include <gcrypt.h>
> +
> +#include "qemu/osdep.h"
> +#include "qemu/host-utils.h"
> +#include "asn1_decoder.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_destroy(QCryptoGcryptRSA *rsa)
> +{
> +    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->algorithm) {
> +    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->algorithm);
> +        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)
> +{
> +    QCryptoAkCipherRSAKey *rsa_key =
> +        qcrypto_akcipher_parse_rsa_private_key(key, keylen);

With my earlier suggestion, you can use

  g_autoptr(QCryptoAkCipherRSAKey) rsa_key = ...

and avoid need for the later  qcrypto_akcipher_free_rsa_key calls.

> +    gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
> +    int ret = -1;
> +    bool compute_mul_inv = false;
> +    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 clear;
> +    }
> +
> +    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
> +                        rsa_key->e.data, rsa_key->e.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    err = gcry_mpi_scan(&d, GCRYMPI_FMT_STD,
> +                        rsa_key->d.data, rsa_key->d.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    err = gcry_mpi_scan(&p, GCRYMPI_FMT_STD,
> +                        rsa_key->p.data, rsa_key->p.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    err = gcry_mpi_scan(&q, GCRYMPI_FMT_STD,
> +                        rsa_key->q.data, rsa_key->q.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    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 clear;
> +    }
> +    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa,  n);
> +    ret = 0;
> +
> +clear:

Same note about using either 'cleanup' or 'error' depending on whether
this is for both successs+failure paths, or just failure path. This
case is a fit for 'cleanup'.

> +    qcrypto_akcipher_free_rsa_key(rsa_key);
> +    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)
> +{
> +
> +    QCryptoAkCipherRSAKey *rsa_key =
> +        qcrypto_akcipher_parse_rsa_public_key(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 clear;
> +    }
> +
> +    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
> +                        rsa_key->e.data, rsa_key->e.len, NULL);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    err = gcry_sexp_build(&rsa->key, NULL,
> +                          "(public-key (rsa (n %m) (e %m)))", n, e);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa, n);
> +    ret = 0;
> +
> +clear:
> +    qcrypto_akcipher_free_rsa_key(rsa_key);
> +    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 =
> +        container_of(akcipher, 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 clear;
> +    }
> +
> +    err = gcry_pk_encrypt(&cipher_sexp, data_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    /* 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 clear;
> +    }
> +
> +    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 clear;
> +        }
> +        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 clear;
> +        }
> +
> +        /* 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 clear;
> +        }
> +        memcpy(out, result, actual_len);
> +        ret = actual_len;
> +    }
> +
> +clear:
> +    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 =
> +        container_of(akcipher, 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 clear;
> +    }
> +
> +    err = gcry_pk_decrypt(&data_sexp, cipher_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    /* 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 clear;
> +        }
> +        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
> +                             &actual_len, data_mpi);
> +        if (gcry_err_code(err) != 0) {
> +            goto clear;
> +        }
> +         if (actual_len > out_len) {
> +            goto clear;
> +        }
> +        /* 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 clear;
> +        }
> +        memcpy(out, result, actual_len);
> +        ret = actual_len;
> +    }
> +
> +clear:
> +    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 =
> +        container_of(akcipher, 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) {
> +        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 clear;
> +    }
> +
> +    err = gcry_pk_sign(&sig_sexp, dgst_sexp, rsa->key);
> +    if (gcry_err_code(err) != 0) {
> +        goto clear;
> +    }
> +
> +    /* 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 clear;
> +    }
> +
> +    result = gcry_sexp_nth_data(sig_sexp_item, 1, &actual_len);
> +    if (!result || actual_len > out_len) {
> +        goto clear;
> +     }
> +    memcpy(out, result, actual_len);
> +    ret = actual_len;
> +
> +clear:
> +    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 =
> +        container_of(akcipher, 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 clear;
> +    }
> +
> +    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 clear;
> +    }
> +
> +    err = gcry_pk_verify(sig_sexp, dgst_sexp, rsa->key);
> +    if (gcry_err_code(err) == 0) {
> +        ret = 0;
> +    }
> +
> +clear:
> +    gcry_sexp_release(dgst_sexp);
> +    gcry_sexp_release(sig_sexp);
> +
> +    return ret;
> +}
> +
> +static int qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher,
> +                                   Error **errp)
> +{
> +    qcrypto_gcrypt_rsa_destroy((QCryptoGcryptRSA *)akcipher);
> +    return 0;
> +}
> +
> +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 err;
> +        }
> +        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 err;
> +        }
> +        break;
> +
> +    default:
> +        error_setg(errp, "Unknown akcipher key type %d", type);

'goto err' here

> +    }
> +
> +    return rsa;
> +
> +err:
> +    qcrypto_gcrypt_rsa_destroy(rsa);
> +    return NULL;
> +}
> +
> +

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] 19+ messages in thread

* Re: [PATCH v4 7/8] tests/crypto: Add test suite for crypto akcipher
  2022-04-11 10:43 ` [PATCH v4 7/8] tests/crypto: Add test suite for crypto akcipher zhenwei pi
@ 2022-04-26 11:57   ` Daniel P. Berrangé
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrangé @ 2022-04-26 11:57 UTC (permalink / raw)
  To: zhenwei pi
  Cc: mst, arei.gonglei, qemu-devel, virtualization, linux-crypto,
	helei.sig11, cohuck, jasowang

On Mon, Apr 11, 2022 at 06:43:26PM +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>
> ---
>  tests/bench/benchmark-crypto-akcipher.c | 161 ++++++
>  tests/bench/meson.build                 |   4 +
>  tests/bench/test_akcipher_keys.inc      | 537 ++++++++++++++++++
>  tests/unit/meson.build                  |   1 +
>  tests/unit/test-crypto-akcipher.c       | 708 ++++++++++++++++++++++++
>  5 files changed, 1411 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/test_akcipher_keys.inc b/tests/bench/test_akcipher_keys.inc
> new file mode 100644
> index 0000000000..7adf218135
> --- /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,

snip

For the patch as is:

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


It could be nice to add another test with some intentionally corrupt
RSA keys with bad DER encoding, as a way to prove that we're handling
errors in DER decoding correctly when faced with malicous data from a
bad guest.

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] 19+ messages in thread

end of thread, other threads:[~2022-04-26 11:57 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-11 10:43 [PATCH v4 0/8] Introduce akcipher service for virtio-crypto zhenwei pi
2022-04-11 10:43 ` [PATCH v4 1/8] virtio-crypto: header update zhenwei pi
2022-04-11 10:43 ` [PATCH v4 2/8] crypto-akcipher: Introduce akcipher types to qapi zhenwei pi
2022-04-26 10:34   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 3/8] crypto: Introduce akcipher crypto class zhenwei pi
2022-04-26 10:41   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 4/8] crypto: add ASN.1 decoder zhenwei pi
2022-04-26 11:38   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 5/8] crypto: Implement RSA algorithm by hogweed zhenwei pi
2022-04-26 11:51   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 6/8] crypto: Implement RSA algorithm by gcrypt zhenwei pi
2022-04-26 11:54   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 7/8] tests/crypto: Add test suite for crypto akcipher zhenwei pi
2022-04-26 11:57   ` Daniel P. Berrangé
2022-04-11 10:43 ` [PATCH v4 8/8] crypto: Introduce RSA algorithm zhenwei pi
2022-04-12  9:47 ` [PATCH v4 0/8] Introduce akcipher service for virtio-crypto Paolo Bonzini
2022-04-12 10:21   ` zhenwei pi
2022-04-21  1:41 ` PING: " zhenwei pi
2022-04-21  8:10   ` Daniel P. Berrangé

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).