2022-05-25 12:47:46

by Lei He

[permalink] [raw]
Subject: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto

v6 -> v7:
- Fix serval build errors for some specific platforms/configurations.
- Use '%zu' instead of '%lu' for size_t parameters.
- AkCipher-gcrypt: avoid setting wrong error messages when parsing RSA
keys.
- AkCipher-benchmark: process constant amount of sign/verify instead
of running sign/verify for a constant duration.

v5 -> v6:
- Fix build errors and codestyles.
- Add parameter 'Error **errp' for qcrypto_akcipher_rsakey_parse.
- Report more detailed errors.
- Fix buffer length check and return values of akcipher-nettle, allows caller to
pass a buffer with larger size than actual needed.

A million thanks to Daniel!

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

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

Thanks to Daniel!

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

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


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


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

This branch is available and maintained by Lei <[email protected]>
https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine

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

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

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

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

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

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

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

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

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

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

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

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

backends/cryptodev-builtin.c | 272 ++++++-
backends/cryptodev-vhost-user.c | 34 +-
backends/cryptodev.c | 32 +-
crypto/akcipher-gcrypt.c.inc | 595 +++++++++++++++
crypto/akcipher-nettle.c.inc | 451 +++++++++++
crypto/akcipher.c | 108 +++
crypto/akcipherpriv.h | 55 ++
crypto/der.c | 189 +++++
crypto/der.h | 81 ++
crypto/meson.build | 6 +
crypto/rsakey-builtin.c.inc | 200 +++++
crypto/rsakey-nettle.c.inc | 158 ++++
crypto/rsakey.c | 44 ++
crypto/rsakey.h | 92 +++
hw/virtio/virtio-crypto.c | 323 ++++++--
include/crypto/akcipher.h | 158 ++++
include/hw/virtio/virtio-crypto.h | 5 +-
include/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 | 137 ++++
tests/bench/meson.build | 1 +
tests/bench/test_akcipher_keys.inc | 537 ++++++++++++++
tests/unit/meson.build | 2 +
tests/unit/test-crypto-akcipher.c | 990 +++++++++++++++++++++++++
tests/unit/test-crypto-der.c | 290 ++++++++
27 files changed, 4854 insertions(+), 146 deletions(-)
create mode 100644 crypto/akcipher-gcrypt.c.inc
create mode 100644 crypto/akcipher-nettle.c.inc
create mode 100644 crypto/akcipher.c
create mode 100644 crypto/akcipherpriv.h
create mode 100644 crypto/der.c
create mode 100644 crypto/der.h
create mode 100644 crypto/rsakey-builtin.c.inc
create mode 100644 crypto/rsakey-nettle.c.inc
create mode 100644 crypto/rsakey.c
create mode 100644 crypto/rsakey.h
create mode 100644 include/crypto/akcipher.h
create mode 100644 tests/bench/benchmark-crypto-akcipher.c
create mode 100644 tests/bench/test_akcipher_keys.inc
create mode 100644 tests/unit/test-crypto-akcipher.c
create mode 100644 tests/unit/test-crypto-der.c

--
2.11.0



2022-05-25 13:00:20

by Lei He

[permalink] [raw]
Subject: [PATCH 6/9] crypto: Implement RSA algorithm by gcrypt

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

-#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#if defined(CONFIG_GCRYPT)
+#include "akcipher-gcrypt.c.inc"
+#elif defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
#include "akcipher-nettle.c.inc"
#else
QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
--
2.11.0


2022-05-25 13:32:28

by Lei He

[permalink] [raw]
Subject: [PATCH 9/9] crypto: Introduce RSA algorithm

From: zhenwei pi <[email protected]>

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/[email protected]"
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 <[email protected]>
Signed-off-by: lei he <[email protected]
---
backends/cryptodev-builtin.c | 272 +++++++++++++++++++++++++++-----
backends/cryptodev-vhost-user.c | 34 +++-
backends/cryptodev.c | 32 ++--
hw/virtio/virtio-crypto.c | 323 ++++++++++++++++++++++++++++++--------
include/hw/virtio/virtio-crypto.h | 5 +-
include/sysemu/cryptodev.h | 83 ++++++++--
6 files changed, 604 insertions(+), 145 deletions(-)

diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 0671bf9f3e..388aedd8df 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "crypto/cipher.h"
+#include "crypto/akcipher.h"
#include "qom/object.h"


@@ -41,11 +42,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN)
typedef struct CryptoDevBackendBuiltinSession {
QCryptoCipher *cipher;
uint8_t direction; /* encryption or decryption */
- uint8_t type; /* cipher? hash? aead? */
+ uint8_t type; /* cipher? hash? aead? akcipher? */
+ QCryptoAkCipher *akcipher;
QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next;
} CryptoDevBackendBuiltinSession;

-/* Max number of symmetric sessions */
+/* Max number of symmetric/asymmetric sessions */
#define MAX_NUM_SESSIONS 256

#define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
@@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
backend->conf.crypto_services =
1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
1u << VIRTIO_CRYPTO_SERVICE_HASH |
- 1u << VIRTIO_CRYPTO_SERVICE_MAC;
+ 1u << VIRTIO_CRYPTO_SERVICE_MAC |
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
+ backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
/*
* Set the Maximum length of crypto request.
* Why this value? Just avoid to overflow when
* memory allocation for each crypto request.
*/
- backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
+ backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo);
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;

@@ -148,6 +152,53 @@ err:
return -1;
}

+static int cryptodev_builtin_get_rsa_hash_algo(
+ int virtio_rsa_hash, Error **errp)
+{
+ switch (virtio_rsa_hash) {
+ case VIRTIO_CRYPTO_RSA_MD5:
+ return QCRYPTO_HASH_ALG_MD5;
+
+ case VIRTIO_CRYPTO_RSA_SHA1:
+ return QCRYPTO_HASH_ALG_SHA1;
+
+ case VIRTIO_CRYPTO_RSA_SHA256:
+ return QCRYPTO_HASH_ALG_SHA256;
+
+ case VIRTIO_CRYPTO_RSA_SHA512:
+ return QCRYPTO_HASH_ALG_SHA512;
+
+ default:
+ error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash);
+ return -1;
+ }
+}
+
+static int cryptodev_builtin_set_rsa_options(
+ int virtio_padding_algo,
+ int virtio_hash_algo,
+ QCryptoAkCipherOptionsRSA *opt,
+ Error **errp)
+{
+ if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+ opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+ opt->hash_alg =
+ cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
+ if (opt->hash_alg < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+ opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+ return 0;
+ }
+
+ error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo);
+ return -1;
+}
+
static int cryptodev_builtin_create_cipher_session(
CryptoDevBackendBuiltin *builtin,
CryptoDevBackendSymSessionInfo *sess_info,
@@ -240,26 +291,89 @@ static int cryptodev_builtin_create_cipher_session(
return index;
}

-static int64_t cryptodev_builtin_sym_create_session(
+static int cryptodev_builtin_create_akcipher_session(
+ CryptoDevBackendBuiltin *builtin,
+ CryptoDevBackendAsymSessionInfo *sess_info,
+ Error **errp)
+{
+ CryptoDevBackendBuiltinSession *sess;
+ QCryptoAkCipher *akcipher;
+ int index;
+ QCryptoAkCipherKeyType type;
+ QCryptoAkCipherOptions opts;
+
+ switch (sess_info->algo) {
+ case VIRTIO_CRYPTO_AKCIPHER_RSA:
+ opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+ if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo,
+ sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) {
+ return -1;
+ }
+ break;
+
+ /* TODO support DSA&ECDSA until qemu crypto framework support these */
+
+ default:
+ error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo);
+ return -1;
+ }
+
+ switch (sess_info->keytype) {
+ case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
+ break;
+
+ default:
+ error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype);
+ return -1;
+ }
+
+ index = cryptodev_builtin_get_unused_session_index(builtin);
+ if (index < 0) {
+ error_setg(errp, "Total number of sessions created exceeds %u",
+ MAX_NUM_SESSIONS);
+ return -1;
+ }
+
+ akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key,
+ sess_info->keylen, errp);
+ if (!akcipher) {
+ return -1;
+ }
+
+ sess = g_new0(CryptoDevBackendBuiltinSession, 1);
+ sess->akcipher = akcipher;
+
+ builtin->sessions[index] = sess;
+
+ return index;
+}
+
+static int64_t cryptodev_builtin_create_session(
CryptoDevBackend *backend,
- CryptoDevBackendSymSessionInfo *sess_info,
+ CryptoDevBackendSessionInfo *sess_info,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend);
- int64_t session_id = -1;
- int ret;
+ CryptoDevBackendSymSessionInfo *sym_sess_info;
+ CryptoDevBackendAsymSessionInfo *asym_sess_info;

switch (sess_info->op_code) {
case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
- ret = cryptodev_builtin_create_cipher_session(
- builtin, sess_info, errp);
- if (ret < 0) {
- return ret;
- } else {
- session_id = ret;
- }
- break;
+ sym_sess_info = &sess_info->u.sym_sess_info;
+ return cryptodev_builtin_create_cipher_session(
+ builtin, sym_sess_info, errp);
+
+ case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+ asym_sess_info = &sess_info->u.asym_sess_info;
+ return cryptodev_builtin_create_akcipher_session(
+ builtin, asym_sess_info, errp);
+
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
default:
@@ -268,50 +382,44 @@ static int64_t cryptodev_builtin_sym_create_session(
return -1;
}

- return session_id;
+ return -1;
}

-static int cryptodev_builtin_sym_close_session(
+static int cryptodev_builtin_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend);
+ CryptoDevBackendBuiltinSession *session;

assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);

- qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
- g_free(builtin->sessions[session_id]);
+ session = builtin->sessions[session_id];
+ if (session->cipher) {
+ qcrypto_cipher_free(session->cipher);
+ } else if (session->akcipher) {
+ qcrypto_akcipher_free(session->akcipher);
+ }
+
+ g_free(session);
builtin->sessions[session_id] = NULL;
return 0;
}

static int cryptodev_builtin_sym_operation(
- CryptoDevBackend *backend,
- CryptoDevBackendSymOpInfo *op_info,
- uint32_t queue_index, Error **errp)
+ CryptoDevBackendBuiltinSession *sess,
+ CryptoDevBackendSymOpInfo *op_info, Error **errp)
{
- CryptoDevBackendBuiltin *builtin =
- CRYPTODEV_BACKEND_BUILTIN(backend);
- CryptoDevBackendBuiltinSession *sess;
int ret;

- if (op_info->session_id >= MAX_NUM_SESSIONS ||
- builtin->sessions[op_info->session_id] == NULL) {
- error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
- op_info->session_id);
- return -VIRTIO_CRYPTO_INVSESS;
- }
-
if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
error_setg(errp,
"Algorithm chain is unsupported for cryptdoev-builtin");
return -VIRTIO_CRYPTO_NOTSUPP;
}

- sess = builtin->sessions[op_info->session_id];
-
if (op_info->iv_len > 0) {
ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
op_info->iv_len, errp);
@@ -333,9 +441,95 @@ static int cryptodev_builtin_sym_operation(
return -VIRTIO_CRYPTO_ERR;
}
}
+
+ return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_builtin_asym_operation(
+ CryptoDevBackendBuiltinSession *sess, uint32_t op_code,
+ CryptoDevBackendAsymOpInfo *op_info, Error **errp)
+{
+ int ret;
+
+ switch (op_code) {
+ case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+ ret = qcrypto_akcipher_encrypt(sess->akcipher,
+ op_info->src, op_info->src_len,
+ op_info->dst, op_info->dst_len, errp);
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+ ret = qcrypto_akcipher_decrypt(sess->akcipher,
+ op_info->src, op_info->src_len,
+ op_info->dst, op_info->dst_len, errp);
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+ ret = qcrypto_akcipher_sign(sess->akcipher,
+ op_info->src, op_info->src_len,
+ op_info->dst, op_info->dst_len, errp);
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+ ret = qcrypto_akcipher_verify(sess->akcipher,
+ op_info->src, op_info->src_len,
+ op_info->dst, op_info->dst_len, errp);
+ break;
+
+ default:
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ if (ret < 0) {
+ if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) {
+ return -VIRTIO_CRYPTO_KEY_REJECTED;
+ }
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ /* Buffer is too short */
+ if (unlikely(ret > op_info->dst_len)) {
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ op_info->dst_len = ret;
+
return VIRTIO_CRYPTO_OK;
}

+static int cryptodev_builtin_operation(
+ CryptoDevBackend *backend,
+ CryptoDevBackendOpInfo *op_info,
+ uint32_t queue_index, Error **errp)
+{
+ CryptoDevBackendBuiltin *builtin =
+ CRYPTODEV_BACKEND_BUILTIN(backend);
+ CryptoDevBackendBuiltinSession *sess;
+ CryptoDevBackendSymOpInfo *sym_op_info;
+ CryptoDevBackendAsymOpInfo *asym_op_info;
+ enum CryptoDevBackendAlgType algtype = op_info->algtype;
+ int ret = -VIRTIO_CRYPTO_ERR;
+
+ if (op_info->session_id >= MAX_NUM_SESSIONS ||
+ builtin->sessions[op_info->session_id] == NULL) {
+ error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
+ op_info->session_id);
+ return -VIRTIO_CRYPTO_INVSESS;
+ }
+
+ sess = builtin->sessions[op_info->session_id];
+ if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
+ sym_op_info = op_info->u.sym_op_info;
+ ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
+ } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
+ asym_op_info = op_info->u.asym_op_info;
+ ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
+ asym_op_info, errp);
+ }
+
+ return ret;
+}
+
static void cryptodev_builtin_cleanup(
CryptoDevBackend *backend,
Error **errp)
@@ -348,7 +542,7 @@ static void cryptodev_builtin_cleanup(

for (i = 0; i < MAX_NUM_SESSIONS; i++) {
if (builtin->sessions[i] != NULL) {
- cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort);
+ cryptodev_builtin_close_session(backend, i, 0, &error_abort);
}
}

@@ -370,9 +564,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void *data)

bc->init = cryptodev_builtin_init;
bc->cleanup = cryptodev_builtin_cleanup;
- bc->create_session = cryptodev_builtin_sym_create_session;
- bc->close_session = cryptodev_builtin_sym_close_session;
- bc->do_sym_op = cryptodev_builtin_sym_operation;
+ bc->create_session = cryptodev_builtin_create_session;
+ bc->close_session = cryptodev_builtin_close_session;
+ bc->do_op = cryptodev_builtin_operation;
}

static const TypeInfo cryptodev_builtin_info = {
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index bedb452474..5443a59153 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -259,7 +259,33 @@ static int64_t cryptodev_vhost_user_sym_create_session(
return -1;
}

-static int cryptodev_vhost_user_sym_close_session(
+static int64_t cryptodev_vhost_user_create_session(
+ CryptoDevBackend *backend,
+ CryptoDevBackendSessionInfo *sess_info,
+ uint32_t queue_index, Error **errp)
+{
+ uint32_t op_code = sess_info->op_code;
+ CryptoDevBackendSymSessionInfo *sym_sess_info;
+
+ switch (op_code) {
+ case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
+ case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
+ case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
+ case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
+ sym_sess_info = &sess_info->u.sym_sess_info;
+ return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
+ queue_index, errp);
+ default:
+ error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+ sess_info->op_code);
+ return -1;
+
+ }
+
+ return -1;
+}
+
+static int cryptodev_vhost_user_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
uint32_t queue_index, Error **errp)
@@ -351,9 +377,9 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)

bc->init = cryptodev_vhost_user_init;
bc->cleanup = cryptodev_vhost_user_cleanup;
- bc->create_session = cryptodev_vhost_user_sym_create_session;
- bc->close_session = cryptodev_vhost_user_sym_close_session;
- bc->do_sym_op = NULL;
+ bc->create_session = cryptodev_vhost_user_create_session;
+ bc->close_session = cryptodev_vhost_user_close_session;
+ bc->do_op = NULL;

object_class_property_add_str(oc, "chardev",
cryptodev_vhost_user_get_chardev,
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
index 2b105e433c..33eb4e1a70 100644
--- a/backends/cryptodev.c
+++ b/backends/cryptodev.c
@@ -72,9 +72,9 @@ void cryptodev_backend_cleanup(
}
}

-int64_t cryptodev_backend_sym_create_session(
+int64_t cryptodev_backend_create_session(
CryptoDevBackend *backend,
- CryptoDevBackendSymSessionInfo *sess_info,
+ CryptoDevBackendSessionInfo *sess_info,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendClass *bc =
@@ -87,7 +87,7 @@ int64_t cryptodev_backend_sym_create_session(
return -1;
}

-int cryptodev_backend_sym_close_session(
+int cryptodev_backend_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
uint32_t queue_index, Error **errp)
@@ -102,16 +102,16 @@ int cryptodev_backend_sym_close_session(
return -1;
}

-static int cryptodev_backend_sym_operation(
+static int cryptodev_backend_operation(
CryptoDevBackend *backend,
- CryptoDevBackendSymOpInfo *op_info,
+ CryptoDevBackendOpInfo *op_info,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendClass *bc =
CRYPTODEV_BACKEND_GET_CLASS(backend);

- if (bc->do_sym_op) {
- return bc->do_sym_op(backend, op_info, queue_index, errp);
+ if (bc->do_op) {
+ return bc->do_op(backend, op_info, queue_index, errp);
}

return -VIRTIO_CRYPTO_ERR;
@@ -123,20 +123,18 @@ int cryptodev_backend_crypto_operation(
uint32_t queue_index, Error **errp)
{
VirtIOCryptoReq *req = opaque;
+ CryptoDevBackendOpInfo *op_info = &req->op_info;
+ enum CryptoDevBackendAlgType algtype = req->flags;

- if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
- CryptoDevBackendSymOpInfo *op_info;
- op_info = req->u.sym_op_info;
-
- return cryptodev_backend_sym_operation(backend,
- op_info, queue_index, errp);
- } else {
+ if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
+ && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
- req->flags);
- return -VIRTIO_CRYPTO_NOTSUPP;
+ algtype);
+
+ return -VIRTIO_CRYPTO_NOTSUPP;
}

- return -VIRTIO_CRYPTO_ERR;
+ return cryptodev_backend_operation(backend, op_info, queue_index, errp);
}

static void
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index dcd80b904d..a98105d9e9 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -83,7 +83,8 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
struct iovec *iov, unsigned int out_num)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
- CryptoDevBackendSymSessionInfo info;
+ CryptoDevBackendSessionInfo info;
+ CryptoDevBackendSymSessionInfo *sym_info;
int64_t session_id;
int queue_index;
uint32_t op_type;
@@ -92,11 +93,13 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,

memset(&info, 0, sizeof(info));
op_type = ldl_le_p(&sess_req->op_type);
- info.op_type = op_type;
info.op_code = opcode;

+ sym_info = &info.u.sym_sess_info;
+ sym_info->op_type = op_type;
+
if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
- ret = virtio_crypto_cipher_session_helper(vdev, &info,
+ ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
&sess_req->u.cipher.para,
&iov, &out_num);
if (ret < 0) {
@@ -105,47 +108,47 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
} else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
size_t s;
/* cipher part */
- ret = virtio_crypto_cipher_session_helper(vdev, &info,
+ ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
&sess_req->u.chain.para.cipher_param,
&iov, &out_num);
if (ret < 0) {
goto err;
}
/* hash part */
- info.alg_chain_order = ldl_le_p(
+ sym_info->alg_chain_order = ldl_le_p(
&sess_req->u.chain.para.alg_chain_order);
- info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
- info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
- if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
- info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
- info.auth_key_len = ldl_le_p(
+ sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
+ sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
+ if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
+ sym_info->hash_alg =
+ ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
+ sym_info->auth_key_len = ldl_le_p(
&sess_req->u.chain.para.u.mac_param.auth_key_len);
- info.hash_result_len = ldl_le_p(
+ sym_info->hash_result_len = ldl_le_p(
&sess_req->u.chain.para.u.mac_param.hash_result_len);
- if (info.auth_key_len > vcrypto->conf.max_auth_key_len) {
+ if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
error_report("virtio-crypto length of auth key is too big: %u",
- info.auth_key_len);
+ sym_info->auth_key_len);
ret = -VIRTIO_CRYPTO_ERR;
goto err;
}
/* get auth key */
- if (info.auth_key_len > 0) {
- DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len);
- info.auth_key = g_malloc(info.auth_key_len);
- s = iov_to_buf(iov, out_num, 0, info.auth_key,
- info.auth_key_len);
- if (unlikely(s != info.auth_key_len)) {
+ if (sym_info->auth_key_len > 0) {
+ sym_info->auth_key = g_malloc(sym_info->auth_key_len);
+ s = iov_to_buf(iov, out_num, 0, sym_info->auth_key,
+ sym_info->auth_key_len);
+ if (unlikely(s != sym_info->auth_key_len)) {
virtio_error(vdev,
"virtio-crypto authenticated key incorrect");
ret = -EFAULT;
goto err;
}
- iov_discard_front(&iov, &out_num, info.auth_key_len);
+ iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
}
- } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
- info.hash_alg = ldl_le_p(
+ } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
+ sym_info->hash_alg = ldl_le_p(
&sess_req->u.chain.para.u.hash_param.algo);
- info.hash_result_len = ldl_le_p(
+ sym_info->hash_result_len = ldl_le_p(
&sess_req->u.chain.para.u.hash_param.hash_result_len);
} else {
/* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
@@ -161,13 +164,10 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
}

queue_index = virtio_crypto_vq2q(queue_id);
- session_id = cryptodev_backend_sym_create_session(
+ session_id = cryptodev_backend_create_session(
vcrypto->cryptodev,
&info, queue_index, &local_err);
if (session_id >= 0) {
- DPRINTF("create session_id=%" PRIu64 " successfully\n",
- session_id);
-
ret = session_id;
} else {
if (local_err) {
@@ -177,11 +177,78 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
}

err:
- g_free(info.cipher_key);
- g_free(info.auth_key);
+ g_free(sym_info->cipher_key);
+ g_free(sym_info->auth_key);
return ret;
}

+static int64_t
+virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
+ struct virtio_crypto_akcipher_create_session_req *sess_req,
+ uint32_t queue_id, uint32_t opcode,
+ struct iovec *iov, unsigned int out_num)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+ CryptoDevBackendSessionInfo info = {0};
+ CryptoDevBackendAsymSessionInfo *asym_info;
+ int64_t session_id;
+ int queue_index;
+ uint32_t algo, keytype, keylen;
+ g_autofree uint8_t *key = NULL;
+ Error *local_err = NULL;
+
+ algo = ldl_le_p(&sess_req->para.algo);
+ keytype = ldl_le_p(&sess_req->para.keytype);
+ keylen = ldl_le_p(&sess_req->para.keylen);
+
+ if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
+ && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {
+ error_report("unsupported asym keytype: %d", keytype);
+ return -VIRTIO_CRYPTO_NOTSUPP;
+ }
+
+ if (keylen) {
+ key = g_malloc(keylen);
+ if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+ virtio_error(vdev, "virtio-crypto asym key incorrect");
+ return -EFAULT;
+ }
+ iov_discard_front(&iov, &out_num, keylen);
+ }
+
+ info.op_code = opcode;
+ asym_info = &info.u.asym_sess_info;
+ asym_info->algo = algo;
+ asym_info->keytype = keytype;
+ asym_info->keylen = keylen;
+ asym_info->key = key;
+ switch (asym_info->algo) {
+ case VIRTIO_CRYPTO_AKCIPHER_RSA:
+ asym_info->u.rsa.padding_algo =
+ ldl_le_p(&sess_req->para.u.rsa.padding_algo);
+ asym_info->u.rsa.hash_algo =
+ ldl_le_p(&sess_req->para.u.rsa.hash_algo);
+ break;
+
+ /* TODO DSA&ECDSA handling */
+
+ default:
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ queue_index = virtio_crypto_vq2q(queue_id);
+ session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
+ queue_index, &local_err);
+ if (session_id < 0) {
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ return session_id;
+}
+
static uint8_t
virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
struct virtio_crypto_destroy_session_req *close_sess_req,
@@ -195,7 +262,7 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
session_id = ldq_le_p(&close_sess_req->session_id);
DPRINTF("close session, id=%" PRIu64 "\n", session_id);

- ret = cryptodev_backend_sym_close_session(
+ ret = cryptodev_backend_close_session(
vcrypto->cryptodev, session_id, queue_id, &local_err);
if (ret == 0) {
status = VIRTIO_CRYPTO_OK;
@@ -260,13 +327,22 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
opcode = ldl_le_p(&ctrl.header.opcode);
queue_id = ldl_le_p(&ctrl.header.queue_id);

+ memset(&input, 0, sizeof(input));
switch (opcode) {
case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
- memset(&input, 0, sizeof(input));
session_id = virtio_crypto_create_sym_session(vcrypto,
&ctrl.u.sym_create_session,
queue_id, opcode,
out_iov, out_num);
+ goto check_session;
+
+ case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+ session_id = virtio_crypto_create_asym_session(vcrypto,
+ &ctrl.u.akcipher_create_session,
+ queue_id, opcode,
+ out_iov, out_num);
+
+check_session:
/* Serious errors, need to reset virtio crypto device */
if (session_id == -EFAULT) {
virtqueue_detach_element(vq, elem, 0);
@@ -290,10 +366,12 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
virtqueue_push(vq, elem, sizeof(input));
virtio_notify(vdev, vq);
break;
+
case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
+ case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
status = virtio_crypto_handle_close_session(vcrypto,
&ctrl.u.destroy_session, queue_id);
/* The status only occupy one byte, we can directly use it */
@@ -311,7 +389,6 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
default:
error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
- memset(&input, 0, sizeof(input));
stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
if (unlikely(s != sizeof(input))) {
@@ -339,28 +416,39 @@ static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq,
req->in_num = 0;
req->in_len = 0;
req->flags = CRYPTODEV_BACKEND_ALG__MAX;
- req->u.sym_op_info = NULL;
+ memset(&req->op_info, 0x00, sizeof(req->op_info));
}

static void virtio_crypto_free_request(VirtIOCryptoReq *req)
{
- if (req) {
- if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
- size_t max_len;
- CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info;
-
- max_len = op_info->iv_len +
- op_info->aad_len +
- op_info->src_len +
- op_info->dst_len +
- op_info->digest_result_len;
-
- /* Zeroize and free request data structure */
- memset(op_info, 0, sizeof(*op_info) + max_len);
+ if (!req) {
+ return;
+ }
+
+ if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
+ size_t max_len;
+ CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info;
+
+ max_len = op_info->iv_len +
+ op_info->aad_len +
+ op_info->src_len +
+ op_info->dst_len +
+ op_info->digest_result_len;
+
+ /* Zeroize and free request data structure */
+ memset(op_info, 0, sizeof(*op_info) + max_len);
+ g_free(op_info);
+ } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
+ CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info;
+ if (op_info) {
+ g_free(op_info->src);
+ g_free(op_info->dst);
+ memset(op_info, 0, sizeof(*op_info));
g_free(op_info);
}
- g_free(req);
}
+
+ g_free(req);
}

static void
@@ -397,6 +485,35 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev,
}
}

+static void
+virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
+ VirtIOCryptoReq *req, int32_t status,
+ CryptoDevBackendAsymOpInfo *asym_op_info)
+{
+ size_t s, len;
+
+ if (status != VIRTIO_CRYPTO_OK) {
+ return;
+ }
+
+ len = asym_op_info->dst_len;
+ if (!len) {
+ return;
+ }
+
+ s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len);
+ if (s != len) {
+ virtio_error(vdev, "virtio-crypto asym dest data incorrect");
+ return;
+ }
+
+ iov_discard_front(&req->in_iov, &req->in_num, len);
+
+ /* For akcipher, dst_len may be changed after operation */
+ req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
+}
+
+
static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
{
VirtIOCrypto *vcrypto = req->vcrypto;
@@ -404,7 +521,10 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)

if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
virtio_crypto_sym_input_data_helper(vdev, req, status,
- req->u.sym_op_info);
+ req->op_info.u.sym_op_info);
+ } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
+ virtio_crypto_akcipher_input_data_helper(vdev, req, status,
+ req->op_info.u.asym_op_info);
}
stb_p(&req->in->status, status);
virtqueue_push(req->vq, &req->elem, req->in_len);
@@ -543,42 +663,101 @@ 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)
{
VirtIOCrypto *vcrypto = request->vcrypto;
@@ -595,8 +774,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
unsigned out_num;
uint32_t opcode;
uint8_t status = VIRTIO_CRYPTO_ERR;
- uint64_t session_id;
- CryptoDevBackendSymOpInfo *sym_op_info = NULL;
+ CryptoDevBackendOpInfo *op_info = &request->op_info;
Error *local_err = NULL;

if (elem->out_num < 1 || elem->in_num < 1) {
@@ -639,15 +817,28 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
request->in_iov = in_iov;

opcode = ldl_le_p(&req.header.opcode);
- session_id = ldq_le_p(&req.header.session_id);
+ op_info->session_id = ldq_le_p(&req.header.session_id);
+ op_info->op_code = opcode;

switch (opcode) {
case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
case VIRTIO_CRYPTO_CIPHER_DECRYPT:
+ op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_SYM;
ret = virtio_crypto_handle_sym_req(vcrypto,
- &req.u.sym_req,
- &sym_op_info,
+ &req.u.sym_req, op_info,
+ out_iov, out_num);
+ goto check_result;
+
+ case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+ case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+ case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+ case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+ op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_ASYM;
+ ret = virtio_crypto_handle_asym_req(vcrypto,
+ &req.u.akcipher_req, op_info,
out_iov, out_num);
+
+check_result:
/* Serious errors, need to reset virtio crypto device */
if (ret == -EFAULT) {
return -1;
@@ -655,11 +846,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
virtio_crypto_free_request(request);
} else {
- sym_op_info->session_id = session_id;

/* Set request's parameter */
- request->flags = CRYPTODEV_BACKEND_ALG_SYM;
- request->u.sym_op_info = sym_op_info;
ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
request, queue_index, &local_err);
if (ret < 0) {
@@ -674,6 +862,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
virtio_crypto_free_request(request);
}
break;
+
case VIRTIO_CRYPTO_HASH:
case VIRTIO_CRYPTO_MAC:
case VIRTIO_CRYPTO_AEAD_ENCRYPT:
@@ -779,6 +968,7 @@ static void virtio_crypto_init_config(VirtIODevice *vdev)
vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l;
vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h;
vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo;
+ vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo;
vcrypto->conf.max_cipher_key_len =
vcrypto->conf.cryptodev->conf.max_cipher_key_len;
vcrypto->conf.max_auth_key_len =
@@ -891,6 +1081,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config)
stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len);
stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len);
stq_le_p(&crypto_cfg.max_size, c->conf.max_size);
+ stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo);

memcpy(config, &crypto_cfg, c->config_size);
}
diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h
index a2228d7b2e..348749f5d5 100644
--- a/include/hw/virtio/virtio-crypto.h
+++ b/include/hw/virtio/virtio-crypto.h
@@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf {
uint32_t mac_algo_l;
uint32_t mac_algo_h;
uint32_t aead_algo;
+ uint32_t akcipher_algo;

/* Maximum length of cipher key */
uint32_t max_cipher_key_len;
@@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq {
size_t in_len;
VirtQueue *vq;
struct VirtIOCrypto *vcrypto;
- union {
- CryptoDevBackendSymOpInfo *sym_op_info;
- } u;
+ CryptoDevBackendOpInfo op_info;
} VirtIOCryptoReq;

typedef struct VirtIOCryptoQueue {
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index f4d4057d4d..37c3a360fd 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -50,13 +50,13 @@ typedef struct CryptoDevBackendClient

enum CryptoDevBackendAlgType {
CRYPTODEV_BACKEND_ALG_SYM,
+ CRYPTODEV_BACKEND_ALG_ASYM,
CRYPTODEV_BACKEND_ALG__MAX,
};

/**
* CryptoDevBackendSymSessionInfo:
*
- * @op_code: operation code (refer to virtio_crypto.h)
* @cipher_alg: algorithm type of CIPHER
* @key_len: byte length of cipher key
* @hash_alg: algorithm type of HASH/MAC
@@ -74,7 +74,6 @@ enum CryptoDevBackendAlgType {
*/
typedef struct CryptoDevBackendSymSessionInfo {
/* corresponding with virtio crypto spec */
- uint32_t op_code;
uint32_t cipher_alg;
uint32_t key_len;
uint32_t hash_alg;
@@ -90,10 +89,35 @@ typedef struct CryptoDevBackendSymSessionInfo {
} 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.11.0


2022-05-25 15:04:08

by Lei He

[permalink] [raw]
Subject: [PATCH 7/9] test/crypto: Add test suite for crypto akcipher

Add unit test and benchmark test for crypto akcipher.

Signed-off-by: lei he <[email protected]>
Signed-off-by: zhenwei pi <[email protected]>
Reviewed-by: Daniel P. BerrangĂ© <[email protected]>
---
crypto/akcipher-nettle.c.inc | 14 +-
tests/bench/benchmark-crypto-akcipher.c | 137 ++++++
tests/bench/meson.build | 1 +
tests/bench/test_akcipher_keys.inc | 537 ++++++++++++++++++++++++
tests/unit/meson.build | 1 +
tests/unit/test-crypto-akcipher.c | 711 ++++++++++++++++++++++++++++++++
6 files changed, 1394 insertions(+), 7 deletions(-)
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/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc
index a7c0c6a1ee..02699e6e6d 100644
--- a/crypto/akcipher-nettle.c.inc
+++ b/crypto/akcipher-nettle.c.inc
@@ -171,14 +171,14 @@ static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher,
int ret = -1;

if (data_len > rsa->pub.size) {
- error_setg(errp, "Plaintext length %zu is greater than key size: %zu"
+ error_setg(errp, "Plaintext length %zu is greater than key size: %zu",
data_len, rsa->pub.size);
return ret;
}

if (enc_len < rsa->pub.size) {
error_setg(errp, "Ciphertext buffer length %zu is less than "
- "key size: %zu", rsa->pub.size);
+ "key size: %zu", enc_len, rsa->pub.size);
return ret;
}

@@ -218,7 +218,7 @@ static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,

if (enc_len > rsa->priv.size) {
error_setg(errp, "Ciphertext length %zu is greater than key size: %zu",
- rsa->priv.size);
+ enc_len, rsa->priv.size);
return ret;
}

@@ -264,13 +264,13 @@ static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher,

if (data_len > rsa->priv.size) {
error_setg(errp, "Data length %zu is greater than key size: %zu",
- rsa->priv.size);
+ data_len, rsa->priv.size);
return ret;
}

if (sig_len < rsa->priv.size) {
error_setg(errp, "Signature buffer length %zu is less than "
- "key size: %zu", rsa->priv.size);
+ "key size: %zu", sig_len, rsa->priv.size);
return ret;
}

@@ -330,12 +330,12 @@ static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher,
}
if (data_len > rsa->pub.size) {
error_setg(errp, "Data length %zu is greater than key size: %zu",
- rsa->pub.size);
+ data_len, rsa->pub.size);
return ret;
}
if (sig_len < rsa->pub.size) {
error_setg(errp, "Signature length %zu is greater than key size: %zu",
- rsa->pub.size);
+ sig_len, rsa->pub.size);
return ret;
}

diff --git a/tests/bench/benchmark-crypto-akcipher.c b/tests/bench/benchmark-crypto-akcipher.c
new file mode 100644
index 0000000000..15e69557ed
--- /dev/null
+++ b/tests/bench/benchmark-crypto-akcipher.c
@@ -0,0 +1,137 @@
+/*
+ * QEMU Crypto akcipher speed benchmark
+ *
+ * Copyright (c) 2022 Bytedance
+ *
+ * Authors:
+ * lei he <[email protected]>
+ *
+ * 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 QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key,
+ size_t keylen,
+ QCryptoRSAPaddingAlgorithm padding,
+ QCryptoHashAlgorithm hash)
+{
+ QCryptoAkCipherOptions opt;
+ QCryptoAkCipher *rsa;
+
+ opt.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+ opt.u.rsa.padding_alg = padding;
+ opt.u.rsa.hash_alg = hash;
+ rsa = qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ priv_key, keylen, &error_abort);
+ return rsa;
+}
+
+static void test_rsa_speed(const uint8_t *priv_key, size_t keylen,
+ size_t key_size)
+{
+#define BYTE 8
+#define SHA1_DGST_LEN 20
+#define SIGN_TIMES 10000
+#define VERIFY_TIMES 100000
+#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1
+#define HASH QCRYPTO_HASH_ALG_SHA1
+
+ g_autoptr(QCryptoAkCipher) rsa =
+ create_rsa_akcipher(priv_key, keylen, PADDING, HASH);
+ g_autofree uint8_t *dgst = NULL;
+ g_autofree uint8_t *signature = NULL;
+ size_t count;
+
+ dgst = g_new0(uint8_t, SHA1_DGST_LEN);
+ memset(dgst, g_test_rand_int(), SHA1_DGST_LEN);
+ signature = g_new0(uint8_t, key_size / BYTE);
+
+ g_test_message("benchmark rsa%zu (%s-%s) sign...", key_size,
+ QCryptoRSAPaddingAlgorithm_str(PADDING),
+ QCryptoHashAlgorithm_str(HASH));
+ g_test_timer_start();
+ for (count = 0; count < SIGN_TIMES; ++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%zu (%s-%s) sign %zu 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%zu (%s-%s) verification...", key_size,
+ QCryptoRSAPaddingAlgorithm_str(PADDING),
+ QCryptoHashAlgorithm_str(HASH));
+ g_test_timer_start();
+ for (count = 0; count < VERIFY_TIMES; ++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%zu (%s-%s) verify %zu times in %.2f seconds,"
+ " %.2f times/sec ",
+ key_size, QCryptoRSAPaddingAlgorithm_str(PADDING),
+ QCryptoHashAlgorithm_str(HASH),
+ count, g_test_timer_last(),
+ (double)count / g_test_timer_last());
+}
+
+static void test_rsa_1024_speed(const void *opaque)
+{
+ size_t key_size = (size_t)opaque;
+ test_rsa_speed(rsa1024_priv_key, sizeof(rsa1024_priv_key), key_size);
+}
+
+static void test_rsa_2048_speed(const void *opaque)
+{
+ size_t key_size = (size_t)opaque;
+ test_rsa_speed(rsa2048_priv_key, sizeof(rsa2048_priv_key), key_size);
+}
+
+static void test_rsa_4096_speed(const void *opaque)
+{
+ size_t key_size = (size_t)opaque;
+ test_rsa_speed(rsa4096_priv_key, sizeof(rsa4096_priv_key), key_size);
+}
+
+int main(int argc, char **argv)
+{
+ char *alg = NULL;
+ char *size = NULL;
+ g_test_init(&argc, &argv, NULL);
+ g_assert(qcrypto_init(NULL) == 0);
+
+#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..279a8fcc33 100644
--- a/tests/bench/meson.build
+++ b/tests/bench/meson.build
@@ -20,6 +20,7 @@ if have_block
'benchmark-crypto-hash': [crypto],
'benchmark-crypto-hmac': [crypto],
'benchmark-crypto-cipher': [crypto],
+ 'benchmark-crypto-akcipher': [crypto],
}
endif

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


2022-05-25 15:51:31

by Lei He

[permalink] [raw]
Subject: [PATCH 4/9] crypto: add ASN.1 DER decoder

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

Signed-off-by: zhenwei pi <[email protected]>
Signed-off-by: lei he <[email protected]>
---
crypto/der.c | 189 ++++++++++++++++++++++++++++
crypto/der.h | 81 ++++++++++++
crypto/meson.build | 1 +
tests/unit/meson.build | 1 +
tests/unit/test-crypto-der.c | 290 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 562 insertions(+)
create mode 100644 crypto/der.c
create mode 100644 crypto/der.h
create mode 100644 tests/unit/test-crypto-der.c

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


2022-05-25 19:46:47

by Lei He

[permalink] [raw]
Subject: [PATCH 8/9] tests/crypto: Add test suite for RSA keys

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

Signed-off-by: lei he <[email protected]>
Reviewed-by: Daniel P. BerrangĂ© <[email protected]>
---
tests/unit/test-crypto-akcipher.c | 285 +++++++++++++++++++++++++++++++++++++-
1 file changed, 282 insertions(+), 3 deletions(-)

diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c
index b5be563884..4f1f4214dd 100644
--- a/tests/unit/test-crypto-akcipher.c
+++ b/tests/unit/test-crypto-akcipher.c
@@ -517,6 +517,158 @@ static const uint8_t exp_ciphertext_rsa2048_pkcs1[] = {
0xd0, 0x28, 0x03, 0x19, 0xa6, 0x06, 0x13, 0x45,
};

+static const uint8_t rsa_private_key_lack_element[] = {
+ /* RSAPrivateKey, offset: 0, length: 176 */
+ 0x30, 0x81, 0xb0,
+ /* version, offset: 4, length: 1 */
+ 0x02, 0x01, 0x00,
+ /* n, offset: 7, length: 65 */
+ 0x02, 0x41,
+ 0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+ 0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+ 0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+ 0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+ 0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+ 0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+ 0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+ 0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+ 0xf7,
+ /* e, offset: 74, length: 3 */
+ 0x02, 0x03, 0x01, 0x00, 0x01,
+ /* d, offset: 79, length: 64 */
+ 0x02, 0x40,
+ 0x1e, 0x80, 0xfe, 0xda, 0x65, 0xdb, 0x70, 0xb8,
+ 0x61, 0x91, 0x28, 0xbf, 0x6c, 0x32, 0xc1, 0x05,
+ 0xd1, 0x26, 0x6a, 0x1c, 0x83, 0xcc, 0xf4, 0x1f,
+ 0x53, 0x42, 0x72, 0x1f, 0x62, 0x57, 0x0a, 0xc4,
+ 0x66, 0x76, 0x30, 0x87, 0xb9, 0xb1, 0xb9, 0x6a,
+ 0x63, 0xfd, 0x8f, 0x3e, 0xfc, 0x35, 0x3f, 0xd6,
+ 0x2e, 0x6c, 0xc8, 0x70, 0x8a, 0x17, 0xc1, 0x28,
+ 0x6a, 0xfe, 0x51, 0x56, 0xb3, 0x92, 0x6f, 0x09,
+ /* p, offset: 145, length: 33 */
+ 0x02, 0x21,
+ 0x00, 0xe3, 0x2e, 0x2d, 0x8d, 0xba, 0x1c, 0x34,
+ 0x4c, 0x49, 0x9f, 0xc1, 0xa6, 0xdd, 0xd7, 0x13,
+ 0x8d, 0x05, 0x48, 0xdd, 0xff, 0x5c, 0x30, 0xbc,
+ 0x6b, 0xc4, 0x18, 0x9d, 0xfc, 0xa2, 0xd0, 0x9b,
+ 0x4d,
+ /* q, offset: 180, length: 33 */
+ 0x02, 0x21,
+ 0x00, 0xd1, 0x75, 0xaf, 0x4b, 0xc6, 0x1a, 0xb0,
+ 0x98, 0x14, 0x42, 0xae, 0x33, 0xf3, 0x44, 0xde,
+ 0x21, 0xcb, 0x04, 0xda, 0xfb, 0x1e, 0x35, 0x92,
+ 0xcd, 0x69, 0xc0, 0x83, 0x06, 0x83, 0x8e, 0x39,
+ 0x53,
+ /* lack element: dp, dq, u */
+};
+
+static const uint8_t rsa_public_key_lack_element[] = {
+ /* RSAPublicKey, offset: 0, length: 67 */
+ 0x30, 0x81, 0x43,
+ /* n, offset: 7, length: 65 */
+ 0x02, 0x41,
+ 0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+ 0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+ 0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+ 0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+ 0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+ 0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+ 0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+ 0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+ 0xf7,
+ /* lack element: e */
+};
+
+static const uint8_t rsa_public_key_empty_element[] = {
+ /* RSAPublicKey, offset: 0, length: 69 */
+ 0x30, 0x81, 0x45,
+ /* n, offset: 7, length: 65 */
+ 0x02, 0x41,
+ 0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+ 0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+ 0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+ 0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+ 0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+ 0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+ 0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+ 0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+ 0xf7,
+ /* e: empty element */
+ 0x02, 0x00,
+};
+
+static const uint8_t rsa_private_key_empty_element[] = {
+ /* RSAPrivateKey, offset: 0, length: 19 */
+ 0x30, 0x81, 0x13,
+ /* version, offset: 4, length: 1 */
+ 0x02, 0x01, 0x00,
+ /* n: empty element */
+ 0x02, 0x00,
+ /* e: empty element */
+ 0x02, 0x00,
+ /* d: empty element */
+ 0x02, 0x00,
+ /* p: empty element */
+ 0x02, 0x00,
+ /* q: empty element */
+ 0x02, 0x00,
+ /* dp: empty element */
+ 0x02, 0x00,
+ /* dq: empty element */
+ 0x02, 0x00,
+ /* u: empty element */
+ 0x02, 0x00,
+};
+
+static const uint8_t rsa_public_key_invalid_length_val[] = {
+ /* RSAPublicKey, INVALID length: 313 */
+ 0x30, 0x82, 0x01, 0x39,
+ /* n, offset: 7, length: 65 */
+ 0x02, 0x41,
+ 0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+ 0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+ 0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+ 0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+ 0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+ 0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+ 0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+ 0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+ 0xf7,
+ /* e, */
+ 0x02, 0x03, 0x01, 0x00, 0x01, /* INTEGER, offset: 74, length: 3 */
+};
+
+static const uint8_t rsa_public_key_extra_elem[] = {
+ /* RSAPublicKey, length: 80 */
+ 0x30, 0x81, 0x50,
+ /* n, offset: 7, length: 65 */
+ 0x02, 0x41,
+ 0x00, 0xb9, 0xe1, 0x22, 0xdb, 0x56, 0x2f, 0xb6,
+ 0xf7, 0xf0, 0x0a, 0x87, 0x43, 0x07, 0x12, 0xdb,
+ 0x6d, 0xb6, 0x2b, 0x41, 0x8d, 0x2c, 0x3c, 0xa5,
+ 0xdd, 0x78, 0x9a, 0x8f, 0xab, 0x8e, 0xf2, 0x4a,
+ 0xc8, 0x34, 0x0c, 0x12, 0x4f, 0x11, 0x90, 0xc6,
+ 0xc2, 0xa5, 0xd0, 0xcd, 0xfb, 0xfc, 0x2c, 0x95,
+ 0x56, 0x82, 0xdf, 0x39, 0xf3, 0x3b, 0x1d, 0x62,
+ 0x26, 0x97, 0xb7, 0x93, 0x25, 0xc7, 0xec, 0x7e,
+ 0xf7,
+ /* e, offset: 74, length: 3 */
+ 0x02, 0x03, 0x01, 0x00, 0x01,
+ /* Additional integer field, length 3 */
+ 0x02, 0x06, 0xe1, 0x22, 0xdb, 0xe1, 0x22, 0xdb,
+};
+
+typedef struct QCryptoRSAKeyTestData QCryptoRSAKeyTestData;
+struct QCryptoRSAKeyTestData {
+ const char *path;
+ QCryptoAkCipherKeyType key_type;
+ QCryptoAkCipherOptions opt;
+ const uint8_t *key;
+ size_t keylen;
+ bool is_valid_key;
+ size_t exp_key_len;
+};
+
typedef struct QCryptoAkCipherTestData QCryptoAkCipherTestData;
struct QCryptoAkCipherTestData {
const char *path;
@@ -537,7 +689,98 @@ struct QCryptoAkCipherTestData {
size_t slen;
};

-static QCryptoAkCipherTestData test_data[] = {
+static QCryptoRSAKeyTestData rsakey_test_data[] = {
+ {
+ .path = "/crypto/akcipher/rsakey-1024-public",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa1024_public_key,
+ .keylen = sizeof(rsa1024_public_key),
+ .is_valid_key = true,
+ .exp_key_len = 128,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-1024-private",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ .key = rsa1024_private_key,
+ .keylen = sizeof(rsa1024_private_key),
+ .is_valid_key = true,
+ .exp_key_len = 128,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-2048-public",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa2048_public_key,
+ .keylen = sizeof(rsa2048_public_key),
+ .is_valid_key = true,
+ .exp_key_len = 256,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-2048-private",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ .key = rsa2048_private_key,
+ .keylen = sizeof(rsa2048_private_key),
+ .is_valid_key = true,
+ .exp_key_len = 256,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-public-lack-elem",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa_public_key_lack_element,
+ .keylen = sizeof(rsa_public_key_lack_element),
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-private-lack-elem",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ .key = rsa_private_key_lack_element,
+ .keylen = sizeof(rsa_private_key_lack_element),
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-public-empty-elem",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa_public_key_empty_element,
+ .keylen = sizeof(rsa_public_key_empty_element),
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-private-empty-elem",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ .key = rsa_private_key_empty_element,
+ .keylen = sizeof(rsa_private_key_empty_element),
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-public-empty-key",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = NULL,
+ .keylen = 0,
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-private-empty-key",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE,
+ .key = NULL,
+ .keylen = 0,
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-public-invalid-length-val",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa_public_key_invalid_length_val,
+ .keylen = sizeof(rsa_public_key_invalid_length_val),
+ .is_valid_key = false,
+ },
+ {
+ .path = "/crypto/akcipher/rsakey-public-extra-elem",
+ .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC,
+ .key = rsa_public_key_extra_elem,
+ .keylen = sizeof(rsa_public_key_extra_elem),
+ .is_valid_key = false,
+ },
+};
+
+static QCryptoAkCipherTestData akcipher_test_data[] = {
/* rsa1024 with raw padding */
{
.path = "/crypto/akcipher/rsa1024-raw",
@@ -697,14 +940,50 @@ static void test_akcipher(const void *opaque)
qcrypto_akcipher_free(priv_key);
}

+static void test_rsakey(const void *opaque)
+{
+ const QCryptoRSAKeyTestData *data = (const QCryptoRSAKeyTestData *)opaque;
+ QCryptoAkCipherOptions opt = {
+ .alg = QCRYPTO_AKCIPHER_ALG_RSA,
+ .u.rsa = {
+ .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1,
+ .hash_alg = QCRYPTO_HASH_ALG_SHA1,
+ }
+ };
+ g_autoptr(QCryptoAkCipher) key = qcrypto_akcipher_new(
+ &opt, data->key_type, data->key, data->keylen, NULL);
+
+ if (!qcrypto_akcipher_supports(&opt)) {
+ return;
+ }
+
+ if (!data->is_valid_key) {
+ g_assert(key == NULL);
+ return;
+ }
+
+ g_assert(key != NULL);
+ g_assert(qcrypto_akcipher_max_ciphertext_len(key) == data->exp_key_len);
+ g_assert(qcrypto_akcipher_max_plaintext_len(key) == data->exp_key_len);
+ g_assert(qcrypto_akcipher_max_signature_len(key) == data->exp_key_len);
+ g_assert(qcrypto_akcipher_max_dgst_len(key) == data->exp_key_len);
+}
+
int main(int argc, char **argv)
{
size_t i;
g_test_init(&argc, &argv, NULL);
g_assert(qcrypto_init(NULL) == 0);

- for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
- g_test_add_data_func(test_data[i].path, &test_data[i], test_akcipher);
+ for (i = 0; i < G_N_ELEMENTS(akcipher_test_data); i++) {
+ g_test_add_data_func(akcipher_test_data[i].path,
+ &akcipher_test_data[i],
+ test_akcipher);
+ }
+ for (i = 0; i < G_N_ELEMENTS(rsakey_test_data); i++) {
+ g_test_add_data_func(rsakey_test_data[i].path,
+ &rsakey_test_data[i],
+ test_rsakey);
}

return g_test_run();
--
2.11.0


2022-05-25 20:03:26

by Lei He

[permalink] [raw]
Subject: [PATCH 5/9] crypto: Implement RSA algorithm by hogweed

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 <[email protected]>
Signed-off-by: zhenwei pi <[email protected]>
---
crypto/akcipher-nettle.c.inc | 451 +++++++++++++++++++++++++++++++++++++++++++
crypto/akcipher.c | 4 +
crypto/meson.build | 4 +
crypto/rsakey-builtin.c.inc | 200 +++++++++++++++++++
crypto/rsakey-nettle.c.inc | 158 +++++++++++++++
crypto/rsakey.c | 44 +++++
crypto/rsakey.h | 92 +++++++++
meson.build | 11 ++
8 files changed, 964 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..a7c0c6a1ee
--- /dev/null
+++ b/crypto/akcipher-nettle.c.inc
@@ -0,0 +1,451 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <nettle/rsa.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/random.h"
+#include "qapi/error.h"
+#include "sysemu/cryptodev.h"
+#include "rsakey.h"
+
+typedef struct QCryptoNettleRSA {
+ QCryptoAkCipher akcipher;
+ struct rsa_public_key pub;
+ struct rsa_private_key priv;
+ QCryptoRSAPaddingAlgorithm padding_alg;
+ QCryptoHashAlgorithm hash_alg;
+} QCryptoNettleRSA;
+
+static void qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher)
+{
+ QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+ if (!rsa) {
+ return;
+ }
+
+ rsa_public_key_clear(&rsa->pub);
+ rsa_private_key_clear(&rsa->priv);
+ g_free(rsa);
+}
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+ const QCryptoAkCipherOptionsRSA *opt,
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen,
+ Error **errp);
+
+QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen,
+ Error **errp)
+{
+ switch (opts->alg) {
+ case QCRYPTO_AKCIPHER_ALG_RSA:
+ return qcrypto_nettle_rsa_new(&opts->u.rsa, type, key, keylen, errp);
+
+ default:
+ error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static void qcrypto_nettle_rsa_set_akcipher_size(QCryptoAkCipher *akcipher,
+ int key_size)
+{
+ akcipher->max_plaintext_len = key_size;
+ akcipher->max_ciphertext_len = key_size;
+ akcipher->max_signature_len = key_size;
+ akcipher->max_dgst_len = key_size;
+}
+
+static int qcrypt_nettle_parse_rsa_private_key(QCryptoNettleRSA *rsa,
+ const uint8_t *key,
+ size_t keylen,
+ Error **errp)
+{
+ g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+ QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen, errp);
+
+ if (!rsa_key) {
+ return -1;
+ }
+
+ nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+ nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.d, rsa_key->d.len, rsa_key->d.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.p, rsa_key->p.len, rsa_key->p.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.q, rsa_key->q.len, rsa_key->q.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.a, rsa_key->dp.len,
+ rsa_key->dp.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.b, rsa_key->dq.len,
+ rsa_key->dq.data);
+ nettle_mpz_init_set_str_256_u(rsa->priv.c, rsa_key->u.len, rsa_key->u.data);
+
+ if (!rsa_public_key_prepare(&rsa->pub)) {
+ error_setg(errp, "Failed to check RSA key");
+ return -1;
+ }
+
+ /**
+ * Since in the kernel's unit test, the p, q, a, b, c of some
+ * private keys is 0, only the simplest length check is done here
+ */
+ if (rsa_key->p.len > 1 &&
+ rsa_key->q.len > 1 &&
+ rsa_key->dp.len > 1 &&
+ rsa_key->dq.len > 1 &&
+ rsa_key->u.len > 1) {
+ if (!rsa_private_key_prepare(&rsa->priv)) {
+ error_setg(errp, "Failed to check RSA key");
+ return -1;
+ }
+ } else {
+ rsa->priv.size = rsa->pub.size;
+ }
+ qcrypto_nettle_rsa_set_akcipher_size(
+ (QCryptoAkCipher *)rsa, rsa->priv.size);
+
+ return 0;
+}
+
+static int qcrypt_nettle_parse_rsa_public_key(QCryptoNettleRSA *rsa,
+ const uint8_t *key,
+ size_t keylen,
+ Error **errp)
+{
+ g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
+ QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen, errp);
+
+ if (!rsa_key) {
+ return -1;
+ }
+ nettle_mpz_init_set_str_256_u(rsa->pub.n, rsa_key->n.len, rsa_key->n.data);
+ nettle_mpz_init_set_str_256_u(rsa->pub.e, rsa_key->e.len, rsa_key->e.data);
+
+ if (!rsa_public_key_prepare(&rsa->pub)) {
+ error_setg(errp, "Failed to check RSA key");
+ return -1;
+ }
+ qcrypto_nettle_rsa_set_akcipher_size(
+ (QCryptoAkCipher *)rsa, rsa->pub.size);
+
+ return 0;
+}
+
+static void wrap_nettle_random_func(void *ctx, size_t len, uint8_t *out)
+{
+ qcrypto_random_bytes(out, len, &error_abort);
+}
+
+static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher,
+ const void *data, size_t data_len,
+ void *enc, size_t enc_len,
+ Error **errp)
+{
+
+ QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+ mpz_t c;
+ int ret = -1;
+
+ if (data_len > rsa->pub.size) {
+ error_setg(errp, "Plaintext length %zu is greater than key size: %zu"
+ data_len, rsa->pub.size);
+ return ret;
+ }
+
+ if (enc_len < rsa->pub.size) {
+ error_setg(errp, "Ciphertext buffer length %zu is less than "
+ "key size: %zu", rsa->pub.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 = nettle_mpz_sizeinbase_256_u(c);
+ }
+ mpz_clear(c);
+ break;
+
+ default:
+ error_setg(errp, "Unknown padding");
+ }
+
+ return ret;
+}
+
+static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher,
+ const void *enc, size_t enc_len,
+ void *data, size_t data_len,
+ Error **errp)
+{
+ QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+ mpz_t c;
+ int ret = -1;
+
+ if (enc_len > rsa->priv.size) {
+ error_setg(errp, "Ciphertext length %zu is greater than key size: %zu",
+ rsa->priv.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:
+ error_setg(errp, "Unknown padding algorithm: %d", rsa->padding_alg);
+ }
+
+ return ret;
+}
+
+static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher,
+ const void *data, size_t data_len,
+ void *sig, size_t sig_len, Error **errp)
+{
+ QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+ int ret = -1, rv;
+ 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 ret;
+ }
+
+ if (data_len > rsa->priv.size) {
+ error_setg(errp, "Data length %zu is greater than key size: %zu",
+ rsa->priv.size);
+ return ret;
+ }
+
+ if (sig_len < rsa->priv.size) {
+ error_setg(errp, "Signature buffer length %zu is less than "
+ "key size: %zu", rsa->priv.size);
+ return ret;
+ }
+
+ mpz_init(s);
+ switch (rsa->hash_alg) {
+ case QCRYPTO_HASH_ALG_MD5:
+ rv = rsa_md5_sign_digest(&rsa->priv, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA1:
+ rv = rsa_sha1_sign_digest(&rsa->priv, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA256:
+ rv = rsa_sha256_sign_digest(&rsa->priv, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA512:
+ rv = rsa_sha512_sign_digest(&rsa->priv, data, s);
+ break;
+
+ default:
+ error_setg(errp, "Unknown hash algorithm: %d", rsa->hash_alg);
+ goto cleanup;
+ }
+
+ if (rv != 1) {
+ error_setg(errp, "Failed to make signature");
+ goto cleanup;
+ }
+ nettle_mpz_get_str_256(sig_len, (uint8_t *)sig, s);
+ ret = nettle_mpz_sizeinbase_256_u(s);
+
+cleanup:
+ mpz_clear(s);
+
+ return ret;
+}
+
+static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher,
+ const void *sig, size_t sig_len,
+ const void *data, size_t data_len,
+ Error **errp)
+{
+ QCryptoNettleRSA *rsa = (QCryptoNettleRSA *)akcipher;
+
+ int ret = -1, rv;
+ 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 verify signature without padding");
+ return ret;
+ }
+ if (data_len > rsa->pub.size) {
+ error_setg(errp, "Data length %zu is greater than key size: %zu",
+ rsa->pub.size);
+ return ret;
+ }
+ if (sig_len < rsa->pub.size) {
+ error_setg(errp, "Signature length %zu is greater than key size: %zu",
+ rsa->pub.size);
+ return ret;
+ }
+
+ nettle_mpz_init_set_str_256_u(s, sig_len, sig);
+ switch (rsa->hash_alg) {
+ case QCRYPTO_HASH_ALG_MD5:
+ rv = rsa_md5_verify_digest(&rsa->pub, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA1:
+ rv = rsa_sha1_verify_digest(&rsa->pub, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA256:
+ rv = rsa_sha256_verify_digest(&rsa->pub, data, s);
+ break;
+
+ case QCRYPTO_HASH_ALG_SHA512:
+ rv = rsa_sha512_verify_digest(&rsa->pub, data, s);
+ break;
+
+ default:
+ error_setg(errp, "Unsupported hash algorithm: %d", rsa->hash_alg);
+ goto cleanup;
+ }
+
+ if (rv != 1) {
+ error_setg(errp, "Failed to verify signature");
+ goto cleanup;
+ }
+ ret = 0;
+
+cleanup:
+ mpz_clear(s);
+
+ return ret;
+}
+
+QCryptoAkCipherDriver nettle_rsa = {
+ .encrypt = qcrypto_nettle_rsa_encrypt,
+ .decrypt = qcrypto_nettle_rsa_decrypt,
+ .sign = qcrypto_nettle_rsa_sign,
+ .verify = qcrypto_nettle_rsa_verify,
+ .free = qcrypto_nettle_rsa_free,
+};
+
+static QCryptoAkCipher *qcrypto_nettle_rsa_new(
+ const QCryptoAkCipherOptionsRSA *opt,
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen,
+ Error **errp)
+{
+ QCryptoNettleRSA *rsa = g_new0(QCryptoNettleRSA, 1);
+
+ rsa->padding_alg = opt->padding_alg;
+ rsa->hash_alg = opt->hash_alg;
+ rsa->akcipher.driver = &nettle_rsa;
+ rsa_public_key_init(&rsa->pub);
+ rsa_private_key_init(&rsa->priv);
+
+ switch (type) {
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ if (qcrypt_nettle_parse_rsa_private_key(rsa, key, keylen, errp) != 0) {
+ goto error;
+ }
+ break;
+
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ if (qcrypt_nettle_parse_rsa_public_key(rsa, key, keylen, errp) != 0) {
+ goto error;
+ }
+ break;
+
+ default:
+ error_setg(errp, "Unknown akcipher key type %d", type);
+ goto error;
+ }
+
+ return (QCryptoAkCipher *)rsa;
+
+error:
+ qcrypto_nettle_rsa_free((QCryptoAkCipher *)rsa);
+ return NULL;
+}
+
+
+bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
+{
+ switch (opts->alg) {
+ case QCRYPTO_AKCIPHER_ALG_RSA:
+ switch (opts->u.rsa.padding_alg) {
+ case QCRYPTO_RSA_PADDING_ALG_PKCS1:
+ switch (opts->u.rsa.hash_alg) {
+ case QCRYPTO_HASH_ALG_MD5:
+ case QCRYPTO_HASH_ALG_SHA1:
+ case QCRYPTO_HASH_ALG_SHA256:
+ case QCRYPTO_HASH_ALG_SHA512:
+ return true;
+
+ default:
+ return false;
+ }
+
+ case QCRYPTO_RSA_PADDING_ALG_RAW:
+ default:
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+}
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index ab28bf415b..f287083f92 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -23,6 +23,9 @@
#include "crypto/akcipher.h"
#include "akcipherpriv.h"

+#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#include "akcipher-nettle.c.inc"
+#else
QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
QCryptoAkCipherKeyType type,
const uint8_t *key, size_t keylen,
@@ -37,6 +40,7 @@ bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
{
return false;
}
+#endif

int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
diff --git a/crypto/meson.build b/crypto/meson.build
index b8152ae7cb..5f03a30d34 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..aeeacc8f9b
--- /dev/null
+++ b/crypto/rsakey-builtin.c.inc
@@ -0,0 +1,200 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "der.h"
+#include "rsakey.h"
+
+static int extract_mpi(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ QCryptoAkCipherMPI *mpi = (QCryptoAkCipherMPI *)ctx;
+ if (vlen == 0) {
+ error_setg(errp, "Empty mpi field");
+ return -1;
+ }
+ mpi->data = g_memdup2(value, vlen);
+ mpi->len = vlen;
+ return 0;
+}
+
+static int extract_version(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ uint8_t *version = (uint8_t *)ctx;
+ if (vlen != 1 || *value > 1) {
+ error_setg(errp, "Invalid rsakey version");
+ return -1;
+ }
+ *version = *value;
+ return 0;
+}
+
+static int extract_seq_content(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ const uint8_t **content = (const uint8_t **)ctx;
+ if (vlen == 0) {
+ error_setg(errp, "Empty sequence");
+ return -1;
+ }
+ *content = value;
+ return 0;
+}
+
+/**
+ *
+ * RsaPubKey ::= SEQUENCE {
+ * n INTEGER
+ * e INTEGER
+ * }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+ const uint8_t *seq;
+ size_t seq_length;
+ int decode_ret;
+
+ decode_ret = qcrypto_der_decode_seq(&key, &keylen,
+ extract_seq_content, &seq, errp);
+ if (decode_ret < 0 || keylen != 0) {
+ goto error;
+ }
+ seq_length = decode_ret;
+
+ if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &rsa->n, errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &rsa->e, errp) < 0) {
+ goto error;
+ }
+ if (seq_length != 0) {
+ goto error;
+ }
+
+ return rsa;
+
+error:
+ if (errp && !*errp) {
+ error_setg(errp, "Invalid RSA public key");
+ }
+ qcrypto_akcipher_rsakey_free(rsa);
+ return NULL;
+}
+
+/**
+ * RsaPrivKey ::= SEQUENCE {
+ * version INTEGER
+ * n INTEGER
+ * e INTEGER
+ * d INTEGER
+ * p INTEGER
+ * q INTEGER
+ * dp INTEGER
+ * dq INTEGER
+ * u INTEGER
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+ uint8_t version;
+ const uint8_t *seq;
+ int decode_ret;
+ size_t seq_length;
+
+ decode_ret = qcrypto_der_decode_seq(&key, &keylen, extract_seq_content,
+ &seq, errp);
+ if (decode_ret < 0 || keylen != 0) {
+ goto error;
+ }
+ seq_length = decode_ret;
+
+ decode_ret = qcrypto_der_decode_int(&seq, &seq_length, extract_version,
+ &version, errp);
+
+ if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &rsa->n, errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &rsa->e, errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &rsa->d, errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->p,
+ errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->q,
+ errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dp,
+ errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->dq,
+ errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi, &rsa->u,
+ errp) < 0) {
+ goto error;
+ }
+
+ /**
+ * According to the standard, otherPrimeInfos must be present for version 1.
+ * There is no strict verification here, this is to be compatible with
+ * the unit test of the kernel. TODO: remove this until linux kernel's
+ * unit-test is fixed.
+ */
+ if (version == 1 && seq_length != 0) {
+ if (qcrypto_der_decode_seq(&seq, &seq_length, NULL, NULL, errp) < 0) {
+ goto error;
+ }
+ if (seq_length != 0) {
+ goto error;
+ }
+ return rsa;
+ }
+ if (seq_length != 0) {
+ goto error;
+ }
+
+ return rsa;
+
+error:
+ if (errp && !*errp) {
+ error_setg(errp, "Invalid RSA private key");
+ }
+ qcrypto_akcipher_rsakey_free(rsa);
+ return NULL;
+}
+
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+ QCryptoAkCipherKeyType type, const uint8_t *key,
+ size_t keylen, Error **errp)
+{
+ switch (type) {
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ return qcrypto_builtin_rsa_private_key_parse(key, keylen, errp);
+
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ return qcrypto_builtin_rsa_public_key_parse(key, keylen, errp);
+
+ default:
+ error_setg(errp, "Unknown key type: %d", type);
+ return NULL;
+ }
+}
diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc
new file mode 100644
index 0000000000..cc49872e78
--- /dev/null
+++ b/crypto/rsakey-nettle.c.inc
@@ -0,0 +1,158 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <[email protected]>
+ *
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "rsakey.h"
+
+static bool DumpMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
+{
+ mpi->data = g_memdup2(i->data, i->length);
+ mpi->len = i->length;
+ return true;
+}
+
+static bool GetMPI(struct asn1_der_iterator *i, QCryptoAkCipherMPI *mpi)
+{
+ if (asn1_der_iterator_next(i) != ASN1_ITERATOR_PRIMITIVE ||
+ i->type != ASN1_INTEGER) {
+ return false;
+ }
+ return DumpMPI(i, mpi);
+}
+
+/**
+ * RsaPrivKey ::= SEQUENCE {
+ * version INTEGER
+ * n INTEGER
+ * e INTEGER
+ * d INTEGER
+ * p INTEGER
+ * q INTEGER
+ * dp INTEGER
+ * dq INTEGER
+ * u INTEGER
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_private_key_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+ struct asn1_der_iterator i;
+ uint32_t version;
+ int tag;
+
+ /* Parse entire struct */
+ if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
+ i.type != ASN1_SEQUENCE ||
+ asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
+ i.type != ASN1_INTEGER ||
+ !asn1_der_get_uint32(&i, &version) ||
+ version > 1 ||
+ !GetMPI(&i, &rsa->n) ||
+ !GetMPI(&i, &rsa->e) ||
+ !GetMPI(&i, &rsa->d) ||
+ !GetMPI(&i, &rsa->p) ||
+ !GetMPI(&i, &rsa->q) ||
+ !GetMPI(&i, &rsa->dp) ||
+ !GetMPI(&i, &rsa->dq) ||
+ !GetMPI(&i, &rsa->u)) {
+ goto error;
+ }
+
+ if (version == 1) {
+ tag = asn1_der_iterator_next(&i);
+ /**
+ * According to the standard otherPrimeInfos must be present for
+ * version 1. There is no strict verification here, this is to be
+ * compatible with the unit test of the kernel. TODO: remove this
+ * until linux-kernel's unit-test is fixed;
+ */
+ if (tag == ASN1_ITERATOR_END) {
+ return rsa;
+ }
+ if (tag != ASN1_ITERATOR_CONSTRUCTED ||
+ i.type != ASN1_SEQUENCE) {
+ goto error;
+ }
+ }
+
+ if (asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
+ goto error;
+ }
+
+ return rsa;
+
+error:
+ error_setg(errp, "Failed to parse RSA private key");
+ qcrypto_akcipher_rsakey_free(rsa);
+ return NULL;
+}
+
+/**
+ * RsaPubKey ::= SEQUENCE {
+ * n INTEGER
+ * e INTEGER
+ * }
+ */
+static QCryptoAkCipherRSAKey *qcrypto_nettle_rsa_public_key_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+
+ QCryptoAkCipherRSAKey *rsa = g_new0(QCryptoAkCipherRSAKey, 1);
+ struct asn1_der_iterator i;
+
+ if (asn1_der_iterator_first(&i, keylen, key) != ASN1_ITERATOR_CONSTRUCTED ||
+ i.type != ASN1_SEQUENCE ||
+ asn1_der_decode_constructed_last(&i) != ASN1_ITERATOR_PRIMITIVE ||
+ !DumpMPI(&i, &rsa->n) ||
+ !GetMPI(&i, &rsa->e) ||
+ asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) {
+ goto error;
+ }
+
+ return rsa;
+
+error:
+ error_setg(errp, "Failed to parse RSA public key");
+ qcrypto_akcipher_rsakey_free(rsa);
+ return NULL;
+}
+
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+ QCryptoAkCipherKeyType type, const uint8_t *key,
+ size_t keylen, Error **errp)
+{
+ switch (type) {
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ return qcrypto_nettle_rsa_private_key_parse(key, keylen, errp);
+
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ return qcrypto_nettle_rsa_public_key_parse(key, keylen, errp);
+
+ default:
+ error_setg(errp, "Unknown key type: %d", type);
+ return NULL;
+ }
+}
diff --git a/crypto/rsakey.c b/crypto/rsakey.c
new file mode 100644
index 0000000000..cc40e072f0
--- /dev/null
+++ b/crypto/rsakey.c
@@ -0,0 +1,44 @@
+/*
+ * QEMU Crypto RSA key parser
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "rsakey.h"
+
+void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
+{
+ if (!rsa_key) {
+ return;
+ }
+ g_free(rsa_key->n.data);
+ g_free(rsa_key->e.data);
+ g_free(rsa_key->d.data);
+ g_free(rsa_key->p.data);
+ g_free(rsa_key->q.data);
+ g_free(rsa_key->dp.data);
+ g_free(rsa_key->dq.data);
+ g_free(rsa_key->u.data);
+ g_free(rsa_key);
+}
+
+#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
+#include "rsakey-nettle.c.inc"
+#else
+#include "rsakey-builtin.c.inc"
+#endif
diff --git a/crypto/rsakey.h b/crypto/rsakey.h
new file mode 100644
index 0000000000..974b76f659
--- /dev/null
+++ b/crypto/rsakey.h
@@ -0,0 +1,92 @@
+/*
+ * QEMU Crypto RSA key parser
+ *
+ * Copyright (c) 2022 Bytedance
+ * Author: lei he <[email protected]>
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+
+typedef struct QCryptoAkCipherRSAKey QCryptoAkCipherRSAKey;
+typedef struct QCryptoAkCipherMPI QCryptoAkCipherMPI;
+
+/**
+ * Multiple precious integer, encoded as two' complement,
+ * copied directly from DER encoded ASN.1 structures.
+ */
+struct QCryptoAkCipherMPI {
+ uint8_t *data;
+ size_t len;
+};
+
+/* See rfc2437: https://datatracker.ietf.org/doc/html/rfc2437 */
+struct QCryptoAkCipherRSAKey {
+ /* The modulus */
+ QCryptoAkCipherMPI n;
+ /* The public exponent */
+ QCryptoAkCipherMPI e;
+ /* The private exponent */
+ QCryptoAkCipherMPI d;
+ /* The first factor */
+ QCryptoAkCipherMPI p;
+ /* The second factor */
+ QCryptoAkCipherMPI q;
+ /* The first factor's exponent */
+ QCryptoAkCipherMPI dp;
+ /* The second factor's exponent */
+ QCryptoAkCipherMPI dq;
+ /* The CRT coefficient */
+ QCryptoAkCipherMPI u;
+};
+
+/**
+ * Parse DER encoded ASN.1 RSA keys, expected ASN.1 schemas:
+ * RsaPrivKey ::= SEQUENCE {
+ * version INTEGER
+ * n INTEGER
+ * e INTEGER
+ * d INTEGER
+ * p INTEGER
+ * q INTEGER
+ * dp INTEGER
+ * dq INTEGER
+ * u INTEGER
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ *
+ * RsaPubKey ::= SEQUENCE {
+ * n INTEGER
+ * e INTEGER
+ * }
+ *
+ * Returns: On success QCryptoAkCipherRSAKey is returned, otherwise returns NULL
+ */
+QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen, Error **errp);
+
+void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
+ qcrypto_akcipher_rsakey_free);
+
+#endif
diff --git a/meson.build b/meson.build
index 40132b4804..68bd083189 100644
--- a/meson.build
+++ b/meson.build
@@ -1098,6 +1098,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()
@@ -1135,6 +1136,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
@@ -1744,6 +1754,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.11.0


2022-05-26 11:44:44

by Daniel P. Berrangé

[permalink] [raw]
Subject: Re: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto

I've sent a pull request containing all the crypto/ changes,
as that covers stuff I maintain. ie patches 2-8

Patches 1 and 9, I'll leave for MST to review & queue since the
virtual hardware is not my area of knowledge.

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

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 :|


2022-05-26 11:53:13

by Gonglei (Arei)

[permalink] [raw]
Subject: RE: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto



> -----Original Message-----
> From: Daniel P. BerrangĂ© [mailto:[email protected]]
> Sent: Thursday, May 26, 2022 6:48 PM
> To: Lei He <[email protected]>
> Cc: [email protected]; Gonglei (Arei) <[email protected]>;
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]
> Subject: Re: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto
>
> I've sent a pull request containing all the crypto/ changes, as that covers stuff I
> maintain. ie patches 2-8
>
> Patches 1 and 9, I'll leave for MST to review & queue since the virtual hardware
> is not my area of knowledge.
>

Thanks for your work, Daniel.

Regards,
-Gonglei

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

2022-05-26 12:03:28

by zhenwei pi

[permalink] [raw]
Subject: Re: Re: [PATCH v7 0/9] Introduce akcipher service for virtio-crypto

Hi, Daniel

Thanks a lot for your review!

On 5/26/22 18:48, Daniel P. Berrangé wrote:
> I've sent a pull request containing all the crypto/ changes,
> as that covers stuff I maintain. ie patches 2-8
>
> Patches 1 and 9, I'll leave for MST to review & queue since the
> virtual hardware is not my area of knowledge.
>
> On Wed, May 25, 2022 at 05:01:09PM +0800, Lei He wrote:
>> v6 -> v7:
>> - Fix serval build errors for some specific platforms/configurations.
>> - Use '%zu' instead of '%lu' for size_t parameters.
>> - AkCipher-gcrypt: avoid setting wrong error messages when parsing RSA
>> keys.
>> - AkCipher-benchmark: process constant amount of sign/verify instead
>> of running sign/verify for a constant duration.
>>
>> v5 -> v6:
>> - Fix build errors and codestyles.
>> - Add parameter 'Error **errp' for qcrypto_akcipher_rsakey_parse.
>> - Report more detailed errors.
>> - Fix buffer length check and return values of akcipher-nettle, allows caller to
>> pass a buffer with larger size than actual needed.
>>
>> A million thanks to Daniel!
>>
>> v4 -> v5:
>> - Move QCryptoAkCipher into akcipherpriv.h, and modify the related comments.
>> - Rename asn1_decoder.c to der.c.
>> - Code style fix: use 'cleanup' & 'error' lables.
>> - Allow autoptr type to auto-free.
>> - Add test cases for rsakey to handle DER error.
>> - Other minor fixes.
>>
>> v3 -> v4:
>> - Coding style fix: Akcipher -> AkCipher, struct XXX -> XXX, Rsa -> RSA,
>> XXX-alg -> XXX-algo.
>> - Change version info in qapi/crypto.json, from 7.0 -> 7.1.
>> - Remove ecdsa from qapi/crypto.json, it would be introduced with the implemetion later.
>> - Use QCryptoHashAlgothrim instead of QCryptoRSAHashAlgorithm(removed) in qapi/crypto.json.
>> - Rename arguments of qcrypto_akcipher_XXX to keep aligned with qcrypto_cipher_XXX(dec/enc/sign/vefiry -> in/out/in2), and add qcrypto_akcipher_max_XXX APIs.
>> - Add new API: qcrypto_akcipher_supports.
>> - Change the return value of qcrypto_akcipher_enc/dec/sign, these functions return the actual length of result.
>> - Separate ASN.1 source code and test case clean.
>> - Disable RSA raw encoding for akcipher-nettle.
>> - Separate RSA key parser into rsakey.{hc}, and implememts it with builtin-asn1-decoder and nettle respectivly.
>> - Implement RSA(pkcs1 and raw encoding) algorithm by gcrypt. This has higher priority than nettle.
>> - For some akcipher operations(eg, decryption of pkcs1pad(rsa)), the length of returned result maybe less than the dst buffer size, return the actual length of result instead of the buffer length to the guest side. (in function virtio_crypto_akcipher_input_data_helper)
>> - Other minor changes.
>>
>> Thanks to Daniel!
>>
>> Eric pointed out this missing part of use case, send it here again.
>>
>> In our plan, the feature is designed for HTTPS offloading case and other applications which use kernel RSA/ecdsa by keyctl syscall. The full picture shows bellow:
>>
>>
>> Nginx/openssl[1] ... Apps
>> Guest -----------------------------------------
>> virtio-crypto driver[2]
>> -------------------------------------------------
>> virtio-crypto backend[3]
>> Host -----------------------------------------
>> / | \
>> builtin[4] vhost keyctl[5] ...
>>
>>
>> [1] User applications can offload RSA calculation to kernel by keyctl syscall. There is no keyctl engine in openssl currently, we developed a engine and tried to contribute it to openssl upstream, but openssl 1.x does not accept new feature. Link:
>> https://github.com/openssl/openssl/pull/16689
>>
>> This branch is available and maintained by Lei <[email protected]>
>> https://github.com/TousakaRin/openssl/tree/OpenSSL_1_1_1-kctl_engine
>>
>> We tested nginx(change config file only) with openssl keyctl engine, it works fine.
>>
>> [2] virtio-crypto driver is used to communicate with host side, send requests to host side to do asymmetric calculation.
>> https://lkml.org/lkml/2022/3/1/1425
>>
>> [3] virtio-crypto backend handles requests from guest side, and forwards request to crypto backend driver of QEMU.
>>
>> [4] Currently RSA is supported only in builtin driver. This driver is supposed to test the full feature without other software(Ex vhost process) and hardware dependence. ecdsa is introduced into qapi type without implementation, this may be implemented in Q3-2022 or later. If ecdsa type definition should be added with the implementation together, I'll remove this in next version.
>>
>> [5] keyctl backend is in development, we will post this feature in Q2-2022. keyctl backend can use hardware acceleration(Ex, Intel QAT).
>>
>> Setup the full environment, tested with Intel QAT on host side, the QPS of HTTPS increase to ~200% in a guest.
>>
>> VS PCI passthrough: the most important benefit of this solution makes the VM migratable.
>>
>> v2 -> v3:
>> - Introduce akcipher types to qapi
>> - Add test/benchmark suite for akcipher class
>> - Seperate 'virtio_crypto: Support virtio crypto asym operation' into:
>> - crypto: Introduce akcipher crypto class
>> - virtio-crypto: Introduce RSA algorithm
>>
>> v1 -> v2:
>> - Update virtio_crypto.h from v2 version of related kernel patch.
>>
>> v1:
>> - Support akcipher for virtio-crypto.
>> - Introduce akcipher class.
>> - Introduce ASN1 decoder into QEMU.
>> - Implement RSA backend by nettle/hogweed.
>>
>> Lei He (6):
>> qapi: crypto-akcipher: Introduce akcipher types to qapi
>> crypto: add ASN.1 DER decoder
>> crypto: Implement RSA algorithm by hogweed
>> crypto: Implement RSA algorithm by gcrypt
>> test/crypto: Add test suite for crypto akcipher
>> tests/crypto: Add test suite for RSA keys
>>
>> Zhenwei Pi (3):
>> virtio-crypto: header update
>> crypto: Introduce akcipher crypto class
>> crypto: Introduce RSA algorithm
>>
>> backends/cryptodev-builtin.c | 272 ++++++-
>> backends/cryptodev-vhost-user.c | 34 +-
>> backends/cryptodev.c | 32 +-
>> crypto/akcipher-gcrypt.c.inc | 595 +++++++++++++++
>> crypto/akcipher-nettle.c.inc | 451 +++++++++++
>> crypto/akcipher.c | 108 +++
>> crypto/akcipherpriv.h | 55 ++
>> crypto/der.c | 189 +++++
>> crypto/der.h | 81 ++
>> crypto/meson.build | 6 +
>> crypto/rsakey-builtin.c.inc | 200 +++++
>> crypto/rsakey-nettle.c.inc | 158 ++++
>> crypto/rsakey.c | 44 ++
>> crypto/rsakey.h | 92 +++
>> hw/virtio/virtio-crypto.c | 323 ++++++--
>> include/crypto/akcipher.h | 158 ++++
>> include/hw/virtio/virtio-crypto.h | 5 +-
>> include/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 | 137 ++++
>> tests/bench/meson.build | 1 +
>> tests/bench/test_akcipher_keys.inc | 537 ++++++++++++++
>> tests/unit/meson.build | 2 +
>> tests/unit/test-crypto-akcipher.c | 990 +++++++++++++++++++++++++
>> tests/unit/test-crypto-der.c | 290 ++++++++
>> 27 files changed, 4854 insertions(+), 146 deletions(-)
>> create mode 100644 crypto/akcipher-gcrypt.c.inc
>> create mode 100644 crypto/akcipher-nettle.c.inc
>> create mode 100644 crypto/akcipher.c
>> create mode 100644 crypto/akcipherpriv.h
>> create mode 100644 crypto/der.c
>> create mode 100644 crypto/der.h
>> create mode 100644 crypto/rsakey-builtin.c.inc
>> create mode 100644 crypto/rsakey-nettle.c.inc
>> create mode 100644 crypto/rsakey.c
>> create mode 100644 crypto/rsakey.h
>> create mode 100644 include/crypto/akcipher.h
>> create mode 100644 tests/bench/benchmark-crypto-akcipher.c
>> create mode 100644 tests/bench/test_akcipher_keys.inc
>> create mode 100644 tests/unit/test-crypto-akcipher.c
>> create mode 100644 tests/unit/test-crypto-der.c
>>
>> --
>> 2.11.0
>>
>
> With regards,
> Daniel

--
zhenwei pi

2022-05-26 22:05:36

by Gonglei (Arei)

[permalink] [raw]
Subject: RE: [PATCH 9/9] crypto: Introduce RSA algorithm



> -----Original Message-----
> From: Lei He [mailto:[email protected]]
> Sent: Wednesday, May 25, 2022 5:01 PM
> To: [email protected]; Gonglei (Arei) <[email protected]>;
> [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: [PATCH 9/9] crypto: Introduce RSA algorithm
>
> From: zhenwei pi <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: lei he <[email protected]
> ---
> backends/cryptodev-builtin.c | 272
> +++++++++++++++++++++++++++-----
> backends/cryptodev-vhost-user.c | 34 +++-
> backends/cryptodev.c | 32 ++--
> hw/virtio/virtio-crypto.c | 323
> ++++++++++++++++++++++++++++++--------
> include/hw/virtio/virtio-crypto.h | 5 +-
> include/sysemu/cryptodev.h | 83 ++++++++--
> 6 files changed, 604 insertions(+), 145 deletions(-)
>
> diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index
> 0671bf9f3e..388aedd8df 100644
> --- a/backends/cryptodev-builtin.c
> +++ b/backends/cryptodev-builtin.c
> @@ -26,6 +26,7 @@
> #include "qapi/error.h"
> #include "standard-headers/linux/virtio_crypto.h"
> #include "crypto/cipher.h"
> +#include "crypto/akcipher.h"
> #include "qom/object.h"
>
>
> @@ -41,11 +42,12 @@
> OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin,
> CRYPTODEV_BACKEND_BUILTIN) typedef struct
> CryptoDevBackendBuiltinSession {
> QCryptoCipher *cipher;
> uint8_t direction; /* encryption or decryption */
> - uint8_t type; /* cipher? hash? aead? */
> + uint8_t type; /* cipher? hash? aead? akcipher? */

Do you actually use the type for akcipher?

> + QCryptoAkCipher *akcipher;
> QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next; }
> CryptoDevBackendBuiltinSession;
>
> -/* Max number of symmetric sessions */
> +/* Max number of symmetric/asymmetric sessions */
> #define MAX_NUM_SESSIONS 256
>
> #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512
> @@ -80,15 +82,17 @@ static void cryptodev_builtin_init(
> backend->conf.crypto_services =
> 1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
> 1u << VIRTIO_CRYPTO_SERVICE_HASH |
> - 1u << VIRTIO_CRYPTO_SERVICE_MAC;
> + 1u << VIRTIO_CRYPTO_SERVICE_MAC |
> + 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
> backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
> backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
> + backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
> /*
> * Set the Maximum length of crypto request.
> * Why this value? Just avoid to overflow when
> * memory allocation for each crypto request.
> */
> - backend->conf.max_size = LONG_MAX -
> sizeof(CryptoDevBackendSymOpInfo);
> + backend->conf.max_size = LONG_MAX -
> sizeof(CryptoDevBackendOpInfo);
> backend->conf.max_cipher_key_len =
> CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
> backend->conf.max_auth_key_len =
> CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
>
> @@ -148,6 +152,53 @@ err:
> return -1;
> }
>
> +static int cryptodev_builtin_get_rsa_hash_algo(
> + int virtio_rsa_hash, Error **errp)
> +{
> + switch (virtio_rsa_hash) {
> + case VIRTIO_CRYPTO_RSA_MD5:
> + return QCRYPTO_HASH_ALG_MD5;
> +
> + case VIRTIO_CRYPTO_RSA_SHA1:
> + return QCRYPTO_HASH_ALG_SHA1;
> +
> + case VIRTIO_CRYPTO_RSA_SHA256:
> + return QCRYPTO_HASH_ALG_SHA256;
> +
> + case VIRTIO_CRYPTO_RSA_SHA512:
> + return QCRYPTO_HASH_ALG_SHA512;
> +
> + default:
> + error_setg(errp, "Unsupported rsa hash algo: %d",
> virtio_rsa_hash);
> + return -1;
> + }
> +}
> +
> +static int cryptodev_builtin_set_rsa_options(
> + int virtio_padding_algo,
> + int virtio_hash_algo,
> + QCryptoAkCipherOptionsRSA *opt,
> + Error **errp)
> +{
> + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
> + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
> + opt->hash_alg =
> + cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp);
> + if (opt->hash_alg < 0) {
> + return -1;
> + }
> + return 0;
> + }
> +
> + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
> + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
> + return 0;
> + }
> +
> + error_setg(errp, "Unsupported rsa padding algo: %d",
> virtio_padding_algo);
> + return -1;
> +}
> +
> static int cryptodev_builtin_create_cipher_session(
> CryptoDevBackendBuiltin *builtin,
> CryptoDevBackendSymSessionInfo *sess_info, @@
> -240,26 +291,89 @@ static int cryptodev_builtin_create_cipher_session(
> return index;
> }
>
> -static int64_t cryptodev_builtin_sym_create_session(
> +static int cryptodev_builtin_create_akcipher_session(
> + CryptoDevBackendBuiltin *builtin,
> + CryptoDevBackendAsymSessionInfo *sess_info,
> + Error **errp)
> +{
> + CryptoDevBackendBuiltinSession *sess;
> + QCryptoAkCipher *akcipher;
> + int index;
> + QCryptoAkCipherKeyType type;
> + QCryptoAkCipherOptions opts;
> +
> + switch (sess_info->algo) {
> + case VIRTIO_CRYPTO_AKCIPHER_RSA:
> + opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
> + if
> (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo,
> + sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) {
> + return -1;
> + }
> + break;
> +
> + /* TODO support DSA&ECDSA until qemu crypto framework support
> these
> + */
> +
> + default:
> + error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo);
> + return -1;
> + }
> +
> + switch (sess_info->keytype) {
> + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
> + type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
> + break;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
> + type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
> + break;
> +
> + default:
> + error_setg(errp, "Unsupported akcipher keytype %u",
> sess_info->keytype);
> + return -1;
> + }
> +
> + index = cryptodev_builtin_get_unused_session_index(builtin);
> + if (index < 0) {
> + error_setg(errp, "Total number of sessions created exceeds %u",
> + MAX_NUM_SESSIONS);
> + return -1;
> + }
> +
> + akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key,
> + sess_info->keylen, errp);
> + if (!akcipher) {
> + return -1;
> + }
> +
> + sess = g_new0(CryptoDevBackendBuiltinSession, 1);
> + sess->akcipher = akcipher;
> +
> + builtin->sessions[index] = sess;
> +
> + return index;
> +}
> +
> +static int64_t cryptodev_builtin_create_session(
> CryptoDevBackend *backend,
> - CryptoDevBackendSymSessionInfo *sess_info,
> + CryptoDevBackendSessionInfo *sess_info,
> uint32_t queue_index, Error **errp) {
> CryptoDevBackendBuiltin *builtin =
> CRYPTODEV_BACKEND_BUILTIN(backend);
> - int64_t session_id = -1;
> - int ret;
> + CryptoDevBackendSymSessionInfo *sym_sess_info;
> + CryptoDevBackendAsymSessionInfo *asym_sess_info;
>
> switch (sess_info->op_code) {
> case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> - ret = cryptodev_builtin_create_cipher_session(
> - builtin, sess_info, errp);
> - if (ret < 0) {
> - return ret;
> - } else {
> - session_id = ret;
> - }
> - break;
> + sym_sess_info = &sess_info->u.sym_sess_info;
> + return cryptodev_builtin_create_cipher_session(
> + builtin, sym_sess_info, errp);
> +
> + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> + asym_sess_info = &sess_info->u.asym_sess_info;
> + return cryptodev_builtin_create_akcipher_session(
> + builtin, asym_sess_info, errp);
> +
> case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
> case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> default:
> @@ -268,50 +382,44 @@ static int64_t
> cryptodev_builtin_sym_create_session(
> return -1;
> }
>
> - return session_id;
> + return -1;
> }
>
> -static int cryptodev_builtin_sym_close_session(
> +static int cryptodev_builtin_close_session(
> CryptoDevBackend *backend,
> uint64_t session_id,
> uint32_t queue_index, Error **errp) {
> CryptoDevBackendBuiltin *builtin =
> CRYPTODEV_BACKEND_BUILTIN(backend);
> + CryptoDevBackendBuiltinSession *session;
>
> assert(session_id < MAX_NUM_SESSIONS &&
> builtin->sessions[session_id]);
>
> - qcrypto_cipher_free(builtin->sessions[session_id]->cipher);
> - g_free(builtin->sessions[session_id]);
> + session = builtin->sessions[session_id];
> + if (session->cipher) {
> + qcrypto_cipher_free(session->cipher);
> + } else if (session->akcipher) {
> + qcrypto_akcipher_free(session->akcipher);
> + }
> +
> + g_free(session);
> builtin->sessions[session_id] = NULL;
> return 0;
> }
>
> static int cryptodev_builtin_sym_operation(
> - CryptoDevBackend *backend,
> - CryptoDevBackendSymOpInfo *op_info,
> - uint32_t queue_index, Error **errp)
> + CryptoDevBackendBuiltinSession *sess,
> + CryptoDevBackendSymOpInfo *op_info, Error **errp)
> {
> - CryptoDevBackendBuiltin *builtin =
> - CRYPTODEV_BACKEND_BUILTIN(backend);
> - CryptoDevBackendBuiltinSession *sess;
> int ret;
>
> - if (op_info->session_id >= MAX_NUM_SESSIONS ||
> - builtin->sessions[op_info->session_id] == NULL) {
> - error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> - op_info->session_id);
> - return -VIRTIO_CRYPTO_INVSESS;
> - }
> -
> if (op_info->op_type ==
> VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
> error_setg(errp,
> "Algorithm chain is unsupported for cryptdoev-builtin");
> return -VIRTIO_CRYPTO_NOTSUPP;
> }
>
> - sess = builtin->sessions[op_info->session_id];
> -
> if (op_info->iv_len > 0) {
> ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv,
> op_info->iv_len, errp); @@ -333,9
> +441,95 @@ static int cryptodev_builtin_sym_operation(
> return -VIRTIO_CRYPTO_ERR;
> }
> }
> +
> + return VIRTIO_CRYPTO_OK;
> +}
> +
> +static int cryptodev_builtin_asym_operation(
> + CryptoDevBackendBuiltinSession *sess, uint32_t
> op_code,
> + CryptoDevBackendAsymOpInfo *op_info, Error **errp) {
> + int ret;
> +
> + switch (op_code) {
> + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
> + ret = qcrypto_akcipher_encrypt(sess->akcipher,
> + op_info->src,
> op_info->src_len,
> + op_info->dst,
> op_info->dst_len, errp);
> + break;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
> + ret = qcrypto_akcipher_decrypt(sess->akcipher,
> + op_info->src,
> op_info->src_len,
> + op_info->dst,
> op_info->dst_len, errp);
> + break;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_SIGN:
> + ret = qcrypto_akcipher_sign(sess->akcipher,
> + op_info->src, op_info->src_len,
> + op_info->dst, op_info->dst_len,
> errp);
> + break;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
> + ret = qcrypto_akcipher_verify(sess->akcipher,
> + op_info->src, op_info->src_len,
> + op_info->dst, op_info->dst_len,
> errp);
> + break;

Please align.


> +
> + 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;
> + }
> +

Pls add exception logs.

> + op_info->dst_len = ret;
> +
> return VIRTIO_CRYPTO_OK;
> }
>
> +static int cryptodev_builtin_operation(
> + CryptoDevBackend *backend,
> + CryptoDevBackendOpInfo *op_info,
> + uint32_t queue_index, Error **errp) {
> + CryptoDevBackendBuiltin *builtin =
> + CRYPTODEV_BACKEND_BUILTIN(backend);
> + CryptoDevBackendBuiltinSession *sess;
> + CryptoDevBackendSymOpInfo *sym_op_info;
> + CryptoDevBackendAsymOpInfo *asym_op_info;
> + enum CryptoDevBackendAlgType algtype = op_info->algtype;
> + int ret = -VIRTIO_CRYPTO_ERR;
> +
> + if (op_info->session_id >= MAX_NUM_SESSIONS ||
> + builtin->sessions[op_info->session_id] == NULL) {
> + error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
> + op_info->session_id);
> + return -VIRTIO_CRYPTO_INVSESS;
> + }
> +
> + sess = builtin->sessions[op_info->session_id];
> + if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
> + sym_op_info = op_info->u.sym_op_info;
> + ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
> + } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
> + asym_op_info = op_info->u.asym_op_info;
> + ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
> + asym_op_info,
> errp);
> + }
> +
> + return ret;
> +}
> +
> static void cryptodev_builtin_cleanup(
> CryptoDevBackend *backend,
> Error **errp)
> @@ -348,7 +542,7 @@ static void cryptodev_builtin_cleanup(
>
> for (i = 0; i < MAX_NUM_SESSIONS; i++) {
> if (builtin->sessions[i] != NULL) {
> - cryptodev_builtin_sym_close_session(backend, i, 0,
> &error_abort);
> + cryptodev_builtin_close_session(backend, i, 0,
> + &error_abort);
> }
> }
>
> @@ -370,9 +564,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void
> *data)
>
> bc->init = cryptodev_builtin_init;
> bc->cleanup = cryptodev_builtin_cleanup;
> - bc->create_session = cryptodev_builtin_sym_create_session;
> - bc->close_session = cryptodev_builtin_sym_close_session;
> - bc->do_sym_op = cryptodev_builtin_sym_operation;
> + bc->create_session = cryptodev_builtin_create_session;
> + bc->close_session = cryptodev_builtin_close_session;
> + bc->do_op = cryptodev_builtin_operation;
> }
>
> static const TypeInfo cryptodev_builtin_info = { diff --git
> a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index
> bedb452474..5443a59153 100644
> --- a/backends/cryptodev-vhost-user.c
> +++ b/backends/cryptodev-vhost-user.c
> @@ -259,7 +259,33 @@ static int64_t
> cryptodev_vhost_user_sym_create_session(
> return -1;
> }
>
> -static int cryptodev_vhost_user_sym_close_session(
> +static int64_t cryptodev_vhost_user_create_session(
> + CryptoDevBackend *backend,
> + CryptoDevBackendSessionInfo *sess_info,
> + uint32_t queue_index, Error **errp) {
> + uint32_t op_code = sess_info->op_code;
> + CryptoDevBackendSymSessionInfo *sym_sess_info;
> +
> + switch (op_code) {
> + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> + case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
> + case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
> + case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
> + sym_sess_info = &sess_info->u.sym_sess_info;
> + return cryptodev_vhost_user_sym_create_session(backend,
> sym_sess_info,
> + queue_index, errp);
> + default:
> + error_setg(errp, "Unsupported opcode :%" PRIu32 "",
> + sess_info->op_code);
> + return -1;
> +
> + }
> +
> + return -1;
> +}
> +
> +static int cryptodev_vhost_user_close_session(
> CryptoDevBackend *backend,
> uint64_t session_id,
> uint32_t queue_index, Error **errp) @@ -351,9 +377,9 @@
> cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)
>
> bc->init = cryptodev_vhost_user_init;
> bc->cleanup = cryptodev_vhost_user_cleanup;
> - bc->create_session = cryptodev_vhost_user_sym_create_session;
> - bc->close_session = cryptodev_vhost_user_sym_close_session;
> - bc->do_sym_op = NULL;
> + bc->create_session = cryptodev_vhost_user_create_session;
> + bc->close_session = cryptodev_vhost_user_close_session;
> + bc->do_op = NULL;
>
> object_class_property_add_str(oc, "chardev",
> cryptodev_vhost_user_get_chardev,
> diff --git a/backends/cryptodev.c b/backends/cryptodev.c index
> 2b105e433c..33eb4e1a70 100644
> --- a/backends/cryptodev.c
> +++ b/backends/cryptodev.c
> @@ -72,9 +72,9 @@ void cryptodev_backend_cleanup(
> }
> }
>
> -int64_t cryptodev_backend_sym_create_session(
> +int64_t cryptodev_backend_create_session(
> CryptoDevBackend *backend,
> - CryptoDevBackendSymSessionInfo *sess_info,
> + CryptoDevBackendSessionInfo *sess_info,
> uint32_t queue_index, Error **errp) {
> CryptoDevBackendClass *bc =
> @@ -87,7 +87,7 @@ int64_t cryptodev_backend_sym_create_session(
> return -1;
> }
>
> -int cryptodev_backend_sym_close_session(
> +int cryptodev_backend_close_session(
> CryptoDevBackend *backend,
> uint64_t session_id,
> uint32_t queue_index, Error **errp) @@ -102,16 +102,16 @@
> int cryptodev_backend_sym_close_session(
> return -1;
> }
>
> -static int cryptodev_backend_sym_operation(
> +static int cryptodev_backend_operation(
> CryptoDevBackend *backend,
> - CryptoDevBackendSymOpInfo *op_info,
> + CryptoDevBackendOpInfo *op_info,
> uint32_t queue_index, Error **errp) {
> CryptoDevBackendClass *bc =
> CRYPTODEV_BACKEND_GET_CLASS(backend);
>
> - if (bc->do_sym_op) {
> - return bc->do_sym_op(backend, op_info, queue_index, errp);
> + if (bc->do_op) {
> + return bc->do_op(backend, op_info, queue_index, errp);
> }
>
> return -VIRTIO_CRYPTO_ERR;
> @@ -123,20 +123,18 @@ int cryptodev_backend_crypto_operation(
> uint32_t queue_index, Error **errp) {
> VirtIOCryptoReq *req = opaque;
> + CryptoDevBackendOpInfo *op_info = &req->op_info;
> + enum CryptoDevBackendAlgType algtype = req->flags;
>
> - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> - CryptoDevBackendSymOpInfo *op_info;
> - op_info = req->u.sym_op_info;
> -
> - return cryptodev_backend_sym_operation(backend,
> - op_info, queue_index, errp);
> - } else {
> + if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
> + && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
> error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
> - req->flags);
> - return -VIRTIO_CRYPTO_NOTSUPP;
> + algtype);
> +
> + return -VIRTIO_CRYPTO_NOTSUPP;
> }
>
> - return -VIRTIO_CRYPTO_ERR;
> + return cryptodev_backend_operation(backend, op_info, queue_index,
> + errp);
> }
>
> static void
> diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index
> dcd80b904d..a98105d9e9 100644
> --- a/hw/virtio/virtio-crypto.c
> +++ b/hw/virtio/virtio-crypto.c
> @@ -83,7 +83,8 @@ virtio_crypto_create_sym_session(VirtIOCrypto
> *vcrypto,
> struct iovec *iov, unsigned int out_num) {
> VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> - CryptoDevBackendSymSessionInfo info;
> + CryptoDevBackendSessionInfo info;
> + CryptoDevBackendSymSessionInfo *sym_info;
> int64_t session_id;
> int queue_index;
> uint32_t op_type;
> @@ -92,11 +93,13 @@ virtio_crypto_create_sym_session(VirtIOCrypto
> *vcrypto,
>
> memset(&info, 0, sizeof(info));
> op_type = ldl_le_p(&sess_req->op_type);
> - info.op_type = op_type;
> info.op_code = opcode;
>
> + sym_info = &info.u.sym_sess_info;
> + sym_info->op_type = op_type;
> +
> if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
> - ret = virtio_crypto_cipher_session_helper(vdev, &info,
> + ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
> &sess_req->u.cipher.para,
> &iov, &out_num);
> if (ret < 0) {
> @@ -105,47 +108,47 @@ virtio_crypto_create_sym_session(VirtIOCrypto
> *vcrypto,
> } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING)
> {
> size_t s;
> /* cipher part */
> - ret = virtio_crypto_cipher_session_helper(vdev, &info,
> + ret = virtio_crypto_cipher_session_helper(vdev, sym_info,
> &sess_req->u.chain.para.cipher_param,
> &iov, &out_num);
> if (ret < 0) {
> goto err;
> }
> /* hash part */
> - info.alg_chain_order = ldl_le_p(
> + sym_info->alg_chain_order = ldl_le_p(
>
> &sess_req->u.chain.para.alg_chain_order);
> - info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
> - info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode);
> - if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
> - info.hash_alg =
> ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
> - info.auth_key_len = ldl_le_p(
> + sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len);
> + sym_info->hash_mode =
> ldl_le_p(&sess_req->u.chain.para.hash_mode);
> + if (sym_info->hash_mode ==
> VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) {
> + sym_info->hash_alg =
> + ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo);
> + sym_info->auth_key_len = ldl_le_p(
>
> &sess_req->u.chain.para.u.mac_param.auth_key_len);
> - info.hash_result_len = ldl_le_p(
> + sym_info->hash_result_len = ldl_le_p(
>
> &sess_req->u.chain.para.u.mac_param.hash_result_len);
> - if (info.auth_key_len > vcrypto->conf.max_auth_key_len) {
> + if (sym_info->auth_key_len >
> + vcrypto->conf.max_auth_key_len) {
> error_report("virtio-crypto length of auth key is too
> big: %u",
> - info.auth_key_len);
> + sym_info->auth_key_len);
> ret = -VIRTIO_CRYPTO_ERR;
> goto err;
> }
> /* get auth key */
> - if (info.auth_key_len > 0) {
> - DPRINTF("auth_keylen=%" PRIu32 "\n",
> info.auth_key_len);
> - info.auth_key = g_malloc(info.auth_key_len);
> - s = iov_to_buf(iov, out_num, 0, info.auth_key,
> - info.auth_key_len);
> - if (unlikely(s != info.auth_key_len)) {
> + if (sym_info->auth_key_len > 0) {
> + sym_info->auth_key = g_malloc(sym_info->auth_key_len);
> + s = iov_to_buf(iov, out_num, 0, sym_info->auth_key,
> + sym_info->auth_key_len);
> + if (unlikely(s != sym_info->auth_key_len)) {
> virtio_error(vdev,
> "virtio-crypto authenticated key incorrect");
> ret = -EFAULT;
> goto err;
> }
> - iov_discard_front(&iov, &out_num, info.auth_key_len);
> + iov_discard_front(&iov, &out_num,
> + sym_info->auth_key_len);
> }
> - } else if (info.hash_mode ==
> VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
> - info.hash_alg = ldl_le_p(
> + } else if (sym_info->hash_mode ==
> VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) {
> + sym_info->hash_alg = ldl_le_p(
>
> &sess_req->u.chain.para.u.hash_param.algo);
> - info.hash_result_len = ldl_le_p(
> + sym_info->hash_result_len = ldl_le_p(
>
> &sess_req->u.chain.para.u.hash_param.hash_result_len);
> } else {
> /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ @@
> -161,13 +164,10 @@ virtio_crypto_create_sym_session(VirtIOCrypto
> *vcrypto,
> }
>
> queue_index = virtio_crypto_vq2q(queue_id);
> - session_id = cryptodev_backend_sym_create_session(
> + session_id = cryptodev_backend_create_session(
> vcrypto->cryptodev,
> &info, queue_index,
> &local_err);
> if (session_id >= 0) {
> - DPRINTF("create session_id=%" PRIu64 " successfully\n",
> - session_id);
> -
> ret = session_id;
> } else {
> if (local_err) {
> @@ -177,11 +177,78 @@ virtio_crypto_create_sym_session(VirtIOCrypto
> *vcrypto,
> }
>
> err:
> - g_free(info.cipher_key);
> - g_free(info.auth_key);
> + g_free(sym_info->cipher_key);
> + g_free(sym_info->auth_key);
> return ret;
> }
>
> +static int64_t
> +virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
> + struct virtio_crypto_akcipher_create_session_req
> *sess_req,
> + uint32_t queue_id, uint32_t opcode,
> + struct iovec *iov, unsigned int out_num) {
> + VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
> + CryptoDevBackendSessionInfo info = {0};
> + CryptoDevBackendAsymSessionInfo *asym_info;
> + int64_t session_id;
> + int queue_index;
> + uint32_t algo, keytype, keylen;
> + g_autofree uint8_t *key = NULL;
> + Error *local_err = NULL;
> +
> + algo = ldl_le_p(&sess_req->para.algo);
> + keytype = ldl_le_p(&sess_req->para.keytype);
> + keylen = ldl_le_p(&sess_req->para.keylen);
> +
> + if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC)
> + && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) {
> + error_report("unsupported asym keytype: %d", keytype);
> + return -VIRTIO_CRYPTO_NOTSUPP;
> + }
> +
> + if (keylen) {
> + key = g_malloc(keylen);
> + if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
> + virtio_error(vdev, "virtio-crypto asym key incorrect");
> + return -EFAULT;
> + }
> + iov_discard_front(&iov, &out_num, keylen);
> + }
> +
> + info.op_code = opcode;
> + asym_info = &info.u.asym_sess_info;
> + asym_info->algo = algo;
> + asym_info->keytype = keytype;
> + asym_info->keylen = keylen;
> + asym_info->key = key;
> + switch (asym_info->algo) {
> + case VIRTIO_CRYPTO_AKCIPHER_RSA:
> + asym_info->u.rsa.padding_algo =
> + ldl_le_p(&sess_req->para.u.rsa.padding_algo);
> + asym_info->u.rsa.hash_algo =
> + ldl_le_p(&sess_req->para.u.rsa.hash_algo);
> + break;
> +
> + /* TODO DSA&ECDSA handling */
> +
> + default:
> + return -VIRTIO_CRYPTO_ERR;
> + }
> +
> + queue_index = virtio_crypto_vq2q(queue_id);
> + session_id = cryptodev_backend_create_session(vcrypto->cryptodev,
> &info,
> + queue_index, &local_err);
> + if (session_id < 0) {
> + if (local_err) {
> + error_report_err(local_err);
> + }
> + return -VIRTIO_CRYPTO_ERR;
> + }
> +
> + return session_id;
> +}
> +
> static uint8_t
> virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
> struct virtio_crypto_destroy_session_req *close_sess_req, @@
> -195,7 +262,7 @@ virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
> session_id = ldq_le_p(&close_sess_req->session_id);
> DPRINTF("close session, id=%" PRIu64 "\n", session_id);
>
> - ret = cryptodev_backend_sym_close_session(
> + ret = cryptodev_backend_close_session(
> vcrypto->cryptodev, session_id, queue_id, &local_err);
> if (ret == 0) {
> status = VIRTIO_CRYPTO_OK;
> @@ -260,13 +327,22 @@ static void virtio_crypto_handle_ctrl(VirtIODevice
> *vdev, VirtQueue *vq)
> opcode = ldl_le_p(&ctrl.header.opcode);
> queue_id = ldl_le_p(&ctrl.header.queue_id);
>
> + memset(&input, 0, sizeof(input));
> switch (opcode) {
> case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
> - memset(&input, 0, sizeof(input));
> session_id = virtio_crypto_create_sym_session(vcrypto,
> &ctrl.u.sym_create_session,
> queue_id, opcode,
> out_iov, out_num);
> + goto check_session;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
> + session_id = virtio_crypto_create_asym_session(vcrypto,
> + &ctrl.u.akcipher_create_session,
> + queue_id, opcode,
> + out_iov, out_num);
> +
> +check_session:
> /* Serious errors, need to reset virtio crypto device */
> if (session_id == -EFAULT) {
> virtqueue_detach_element(vq, elem, 0); @@ -290,10
> +366,12 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev,
> VirtQueue *vq)
> virtqueue_push(vq, elem, sizeof(input));
> virtio_notify(vdev, vq);
> break;
> +
> case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
> case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
> case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
> case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
> + case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
> status = virtio_crypto_handle_close_session(vcrypto,
> &ctrl.u.destroy_session, queue_id);
> /* The status only occupy one byte, we can directly use it */
> @@ -311,7 +389,6 @@ static void virtio_crypto_handle_ctrl(VirtIODevice
> *vdev, VirtQueue *vq)
> case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
> default:
> error_report("virtio-crypto unsupported ctrl opcode: %d",
> opcode);
> - memset(&input, 0, sizeof(input));
> stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
> s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
> if (unlikely(s != sizeof(input))) { @@ -339,28 +416,39 @@ static
> void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq,
> req->in_num = 0;
> req->in_len = 0;
> req->flags = CRYPTODEV_BACKEND_ALG__MAX;
> - req->u.sym_op_info = NULL;
> + memset(&req->op_info, 0x00, sizeof(req->op_info));
> }
>
> static void virtio_crypto_free_request(VirtIOCryptoReq *req) {
> - if (req) {
> - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> - size_t max_len;
> - CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info;
> -
> - max_len = op_info->iv_len +
> - op_info->aad_len +
> - op_info->src_len +
> - op_info->dst_len +
> - op_info->digest_result_len;
> -
> - /* Zeroize and free request data structure */
> - memset(op_info, 0, sizeof(*op_info) + max_len);
> + if (!req) {
> + return;
> + }
> +
> + if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> + size_t max_len;
> + CryptoDevBackendSymOpInfo *op_info =
> + req->op_info.u.sym_op_info;
> +
> + max_len = op_info->iv_len +
> + op_info->aad_len +
> + op_info->src_len +
> + op_info->dst_len +
> + op_info->digest_result_len;
> +
> + /* Zeroize and free request data structure */
> + memset(op_info, 0, sizeof(*op_info) + max_len);
> + g_free(op_info);
> + } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
> + CryptoDevBackendAsymOpInfo *op_info =
> req->op_info.u.asym_op_info;
> + if (op_info) {
> + g_free(op_info->src);
> + g_free(op_info->dst);
> + memset(op_info, 0, sizeof(*op_info));
> g_free(op_info);
> }
> - g_free(req);
> }
> +
> + g_free(req);
> }
>
> static void
> @@ -397,6 +485,35 @@ virtio_crypto_sym_input_data_helper(VirtIODevice
> *vdev,
> }
> }
>
> +static void
> +virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
> + VirtIOCryptoReq *req, int32_t status,
> + CryptoDevBackendAsymOpInfo *asym_op_info) {
> + size_t s, len;
> +
> + if (status != VIRTIO_CRYPTO_OK) {
> + return;
> + }
> +
> + len = asym_op_info->dst_len;
> + if (!len) {
> + return;
> + }
> +
> + s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len);
> + if (s != len) {
> + virtio_error(vdev, "virtio-crypto asym dest data incorrect");
> + return;
> + }
> +
> + iov_discard_front(&req->in_iov, &req->in_num, len);
> +
> + /* For akcipher, dst_len may be changed after operation */
> + req->in_len = sizeof(struct virtio_crypto_inhdr) +
> +asym_op_info->dst_len; }
> +
> +
> static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
> {
> VirtIOCrypto *vcrypto = req->vcrypto; @@ -404,7 +521,10 @@ static
> void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
>
> if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
> virtio_crypto_sym_input_data_helper(vdev, req, status,
> - req->u.sym_op_info);
> +
> req->op_info.u.sym_op_info);
> + } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) {
> + virtio_crypto_akcipher_input_data_helper(vdev, req, status,
> +
> + req->op_info.u.asym_op_info);
> }
> stb_p(&req->in->status, status);
> virtqueue_push(req->vq, &req->elem, req->in_len); @@ -543,42
> +663,101 @@ 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) {
> VirtIOCrypto *vcrypto = request->vcrypto; @@ -595,8 +774,7 @@
> virtio_crypto_handle_request(VirtIOCryptoReq *request)
> unsigned out_num;
> uint32_t opcode;
> uint8_t status = VIRTIO_CRYPTO_ERR;
> - uint64_t session_id;
> - CryptoDevBackendSymOpInfo *sym_op_info = NULL;
> + CryptoDevBackendOpInfo *op_info = &request->op_info;
> Error *local_err = NULL;
>
> if (elem->out_num < 1 || elem->in_num < 1) { @@ -639,15 +817,28 @@
> virtio_crypto_handle_request(VirtIOCryptoReq *request)
> request->in_iov = in_iov;
>
> opcode = ldl_le_p(&req.header.opcode);
> - session_id = ldq_le_p(&req.header.session_id);
> + op_info->session_id = ldq_le_p(&req.header.session_id);
> + op_info->op_code = opcode;
>
> switch (opcode) {
> case VIRTIO_CRYPTO_CIPHER_ENCRYPT:
> case VIRTIO_CRYPTO_CIPHER_DECRYPT:
> + op_info->algtype = request->flags =
> CRYPTODEV_BACKEND_ALG_SYM;
> ret = virtio_crypto_handle_sym_req(vcrypto,
> - &req.u.sym_req,
> - &sym_op_info,
> + &req.u.sym_req, op_info,
> + out_iov, out_num);
> + goto check_result;
> +
> + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
> + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
> + case VIRTIO_CRYPTO_AKCIPHER_SIGN:
> + case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
> + op_info->algtype = request->flags =
> CRYPTODEV_BACKEND_ALG_ASYM;
> + ret = virtio_crypto_handle_asym_req(vcrypto,
> + &req.u.akcipher_req, op_info,
> out_iov, out_num);
> +
> +check_result:
> /* Serious errors, need to reset virtio crypto device */
> if (ret == -EFAULT) {
> return -1;
> @@ -655,11 +846,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq
> *request)
> virtio_crypto_req_complete(request,
> VIRTIO_CRYPTO_NOTSUPP);
> virtio_crypto_free_request(request);
> } else {
> - sym_op_info->session_id = session_id;
>
> /* Set request's parameter */
> - request->flags = CRYPTODEV_BACKEND_ALG_SYM;
> - request->u.sym_op_info = sym_op_info;
> ret =
> cryptodev_backend_crypto_operation(vcrypto->cryptodev,
> request, queue_index,
> &local_err);
> if (ret < 0) {
> @@ -674,6 +862,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq
> *request)
> virtio_crypto_free_request(request);
> }
> break;
> +
> case VIRTIO_CRYPTO_HASH:
> case VIRTIO_CRYPTO_MAC:
> case VIRTIO_CRYPTO_AEAD_ENCRYPT:
> @@ -779,6 +968,7 @@ static void virtio_crypto_init_config(VirtIODevice
> *vdev)
> vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l;
> vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h;
> vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo;
> + vcrypto->conf.akcipher_algo =
> + vcrypto->conf.cryptodev->conf.akcipher_algo;
> vcrypto->conf.max_cipher_key_len =
> vcrypto->conf.cryptodev->conf.max_cipher_key_len;
> vcrypto->conf.max_auth_key_len =
> @@ -891,6 +1081,7 @@ static void virtio_crypto_get_config(VirtIODevice
> *vdev, uint8_t *config)
> stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len);
> stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len);
> stq_le_p(&crypto_cfg.max_size, c->conf.max_size);
> + stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo);
>
> memcpy(config, &crypto_cfg, c->config_size); } diff --git
> a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h
> index a2228d7b2e..348749f5d5 100644
> --- a/include/hw/virtio/virtio-crypto.h
> +++ b/include/hw/virtio/virtio-crypto.h
> @@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf {
> uint32_t mac_algo_l;
> uint32_t mac_algo_h;
> uint32_t aead_algo;
> + uint32_t akcipher_algo;
>
> /* Maximum length of cipher key */
> uint32_t max_cipher_key_len;
> @@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq {
> size_t in_len;
> VirtQueue *vq;
> struct VirtIOCrypto *vcrypto;
> - union {
> - CryptoDevBackendSymOpInfo *sym_op_info;
> - } u;
> + CryptoDevBackendOpInfo op_info;
> } VirtIOCryptoReq;
>
> typedef struct VirtIOCryptoQueue {
> diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index
> f4d4057d4d..37c3a360fd 100644
> --- a/include/sysemu/cryptodev.h
> +++ b/include/sysemu/cryptodev.h
> @@ -50,13 +50,13 @@ typedef struct CryptoDevBackendClient
>
> enum CryptoDevBackendAlgType {
> CRYPTODEV_BACKEND_ALG_SYM,
> + CRYPTODEV_BACKEND_ALG_ASYM,
> CRYPTODEV_BACKEND_ALG__MAX,
> };
>
> /**
> * CryptoDevBackendSymSessionInfo:
> *
> - * @op_code: operation code (refer to virtio_crypto.h)
> * @cipher_alg: algorithm type of CIPHER
> * @key_len: byte length of cipher key
> * @hash_alg: algorithm type of HASH/MAC @@ -74,7 +74,6 @@ enum
> CryptoDevBackendAlgType {
> */
> typedef struct CryptoDevBackendSymSessionInfo {
> /* corresponding with virtio crypto spec */
> - uint32_t op_code;
> uint32_t cipher_alg;
> uint32_t key_len;
> uint32_t hash_alg;
> @@ -90,10 +89,35 @@ typedef struct CryptoDevBackendSymSessionInfo { }
> 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.11.0