Add helper functions for x509 certificate which will be used in the next
patch for the certificate store.

Signed-off-by: Zhuoying Cai <zy...@linux.ibm.com>
---
 crypto/meson.build          |   5 +-
 crypto/x509-utils.c         | 166 ++++++++++++++++++++++++++++++++++++
 include/crypto/x509-utils.h |  54 ++++++++++++
 qapi/crypto.json            |  80 +++++++++++++++++
 4 files changed, 301 insertions(+), 4 deletions(-)

diff --git a/crypto/meson.build b/crypto/meson.build
index 735635de1f..0614bfa914 100644
--- a/crypto/meson.build
+++ b/crypto/meson.build
@@ -22,12 +22,9 @@ crypto_ss.add(files(
   'tlscredsx509.c',
   'tlssession.c',
   'rsakey.c',
+  'x509-utils.c',
 ))
 
-if gnutls.found()
-  crypto_ss.add(files('x509-utils.c'))
-endif
-
 if nettle.found()
   crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 
'pbkdf-nettle.c'))
   if hogweed.found()
diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
index 8bad00a51b..7a7f12c111 100644
--- a/crypto/x509-utils.c
+++ b/crypto/x509-utils.c
@@ -11,6 +11,8 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "crypto/x509-utils.h"
+
+#ifdef CONFIG_GNUTLS
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
 #include <gnutls/x509.h>
@@ -25,6 +27,109 @@ static const int 
qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
     [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
 };
 
+static const int qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS__MAX] = 
{
+    [QCRYPTO_KEYID_FLAGS_SHA1] = GNUTLS_KEYID_USE_SHA1,
+    [QCRYPTO_KEYID_FLAGS_SHA256] = GNUTLS_KEYID_USE_SHA256,
+    [QCRYPTO_KEYID_FLAGS_SHA512] = GNUTLS_KEYID_USE_SHA512,
+    [QCRYPTO_KEYID_FLAGS_BEST_KNOWN] = GNUTLS_KEYID_USE_BEST_KNOWN,
+};
+
+static const int qcrypto_to_gnutls_cert_fmt_map[QCRYPTO_CERT_FMT__MAX] = {
+    [QCRYPTO_CERT_FMT_DER] = GNUTLS_X509_FMT_DER,
+    [QCRYPTO_CERT_FMT_PEM] = GNUTLS_X509_FMT_PEM,
+};
+
+static const int gnutls_to_qcrypto_sig_alg_map[QCRYPTO_SIG_ALGO__MAX] = {
+    [GNUTLS_SIGN_UNKNOWN] = QCRYPTO_SIG_ALGO_UNKNOWN,
+    [GNUTLS_SIGN_RSA_SHA1] = QCRYPTO_SIG_ALGO_RSA_SHA1,
+    [GNUTLS_SIGN_RSA_SHA] = QCRYPTO_SIG_ALGO_RSA_SHA1,
+    [GNUTLS_SIGN_DSA_SHA1] = QCRYPTO_SIG_ALGO_DSA_SHA1,
+    [GNUTLS_SIGN_RSA_MD5] = QCRYPTO_SIG_ALGO_RSA_MD5,
+    [GNUTLS_SIGN_RSA_MD2] = QCRYPTO_SIG_ALGO_RSA_MD2,
+    [GNUTLS_SIGN_RSA_RMD160] = QCRYPTO_SIG_ALGO_RSA_RMD160,
+    [GNUTLS_SIGN_RSA_SHA256] = QCRYPTO_SIG_ALGO_RSA_SHA256,
+    [GNUTLS_SIGN_RSA_SHA384] = QCRYPTO_SIG_ALGO_RSA_SHA384,
+    [GNUTLS_SIGN_RSA_SHA512] = QCRYPTO_SIG_ALGO_RSA_SHA512,
+    [GNUTLS_SIGN_RSA_SHA224] = QCRYPTO_SIG_ALGO_RSA_SHA224,
+    [GNUTLS_SIGN_DSA_SHA224] = QCRYPTO_SIG_ALGO_DSA_SHA224,
+    [GNUTLS_SIGN_DSA_SHA256] = QCRYPTO_SIG_ALGO_DSA_SHA256,
+    [GNUTLS_SIGN_ECDSA_SHA1] = QCRYPTO_SIG_ALGO_ECDSA_SHA1,
+    [GNUTLS_SIGN_ECDSA_SHA224] = QCRYPTO_SIG_ALGO_ECDSA_SHA224,
+    [GNUTLS_SIGN_ECDSA_SHA256] = QCRYPTO_SIG_ALGO_ECDSA_SHA256,
+    [GNUTLS_SIGN_ECDSA_SHA384] = QCRYPTO_SIG_ALGO_ECDSA_SHA384,
+    [GNUTLS_SIGN_ECDSA_SHA512] = QCRYPTO_SIG_ALGO_ECDSA_SHA512,
+};
+
+int qcrypto_check_x509_cert_fmt(uint8_t *cert, size_t size,
+                                QCryptoCertFmt fmt, Error **errp)
+{
+    int rc;
+    int ret = -1;
+    gnutls_x509_crt_t crt;
+    gnutls_datum_t datum = {.data = cert, .size = size};
+
+    if (fmt >= G_N_ELEMENTS(qcrypto_to_gnutls_cert_fmt_map)) {
+        error_setg(errp, "Unknown certificate format");
+        return ret;
+    }
+
+    if (gnutls_x509_crt_init(&crt) < 0) {
+        error_setg(errp, "Failed to initialize certificate");
+        return ret;
+    }
+
+    rc = gnutls_x509_crt_import(crt, &datum, 
qcrypto_to_gnutls_cert_fmt_map[fmt]);
+    if (rc == GNUTLS_E_ASN1_TAG_ERROR) {
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    gnutls_x509_crt_deinit(crt);
+    return ret;
+}
+
+int qcrypto_get_x509_hash_len(QCryptoHashAlgo alg)
+{
+    if (alg >= G_N_ELEMENTS(qcrypto_to_gnutls_hash_alg_map)) {
+        return 0;
+    }
+
+    return gnutls_hash_get_len(qcrypto_to_gnutls_hash_alg_map[alg]);
+}
+
+int qcrypto_get_x509_keyid_len(QCryptoKeyidFlags flag)
+{
+    QCryptoHashAlgo alg;
+
+    if (flag >= G_N_ELEMENTS(qcrypto_to_gnutls_keyid_flags_map)) {
+        return 0;
+    }
+
+    alg = QCRYPTO_HASH_ALGO_SHA1;
+    if ((flag & qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_SHA512]) 
||
+        (flag & 
qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_BEST_KNOWN])) {
+        alg = QCRYPTO_HASH_ALGO_SHA512;
+    } else if (flag & 
qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_SHA256]) {
+        alg = QCRYPTO_HASH_ALGO_SHA256;
+    }
+
+    return qcrypto_get_x509_hash_len(alg);
+}
+
+static int qcrypto_import_x509_cert(gnutls_x509_crt_t crt, gnutls_datum_t 
*datum)
+{
+    int rc;
+
+    rc = gnutls_x509_crt_import(crt, datum, GNUTLS_X509_FMT_PEM);
+    if (rc) {
+        rc = gnutls_x509_crt_import(crt, datum, GNUTLS_X509_FMT_DER);
+    }
+
+    return rc;
+}
+
 int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
                                       QCryptoHashAlgo alg,
                                       uint8_t *result,
@@ -74,3 +179,64 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t 
size,
     gnutls_x509_crt_deinit(crt);
     return ret;
 }
+
+int qcrypto_get_x509_signature_algorithm(uint8_t *cert, size_t size, Error 
**errp)
+{
+    int rc = -1;
+    gnutls_x509_crt_t crt;
+    gnutls_datum_t datum = {.data = cert, .size = size};
+
+    if (gnutls_x509_crt_init(&crt) < 0) {
+        error_setg(errp, "Failed to initialize certificate");
+        return rc;
+    }
+
+    if (qcrypto_import_x509_cert(crt, &datum) != 0) {
+        error_setg(errp, "Failed to import certificate");
+        goto cleanup;
+    }
+
+    rc = gnutls_x509_crt_get_signature_algorithm(crt);
+    rc = gnutls_to_qcrypto_sig_alg_map[rc];
+
+cleanup:
+    gnutls_x509_crt_deinit(crt);
+    return rc;
+}
+
+#else /* ! CONFIG_GNUTLS */
+
+int qcrypto_check_x509_cert_fmt(uint8_t *cert, size_t size,
+                                QCryptoCertFmt fmt, Error **errp)
+{
+    error_setg(errp, "GNUTLS is required to get certificate format");
+    return -ENOTSUP;
+}
+
+int qcrypto_get_x509_hash_len(QCryptoHashAlgo alg)
+{
+    return -ENOTSUP;
+}
+
+int qcrypto_get_x509_keyid_len(QCryptoKeyidFlags flag)
+{
+    return -ENOTSUP;
+}
+
+int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
+                                      QCryptoHashAlgo hash,
+                                      uint8_t *result,
+                                      size_t *resultlen,
+                                      Error **errp)
+{
+    error_setg(errp, "GNUTLS is required to get fingerprint");
+    return -ENOTSUP;
+}
+
+int qcrypto_get_x509_signature_algorithm(uint8_t *cert, size_t size, Error 
**errp)
+{
+    error_setg(errp, "GNUTLS is required to get signature algorithm");
+    return -ENOTSUP;
+}
+
+#endif /* ! CONFIG_GNUTLS */
diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h
index 1e99661a71..d7be57c8ce 100644
--- a/include/crypto/x509-utils.h
+++ b/include/crypto/x509-utils.h
@@ -19,4 +19,58 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t 
size,
                                       size_t *resultlen,
                                       Error **errp);
 
+/**
+ * qcrypto_check_x509_cert_fmt
+ * @cert: pointer to the raw certiricate data
+ * @size: size of the certificate
+ * @fmt: expected certificate format
+ * @errp: error pointer
+ *
+ * Check whether the format of @cert matches @fmt.
+ *
+ * Returns: 0 if the format of @cert matches @fmt,
+           -1 if the format does not match,
+ *         -ENOTSUP if GNUTLS is not enabled.
+ */
+int qcrypto_check_x509_cert_fmt(uint8_t *cert, size_t size,
+                                QCryptoCertFmt fmt, Error **errp);
+
+/**
+ * qcrypto_get_x509_hash_len
+ * @alg: the hash algorithm
+ *
+ * Determine the length of the hash of the given @alg.
+ *
+ * Returns: the length on success,
+            0 on error,
+            -ENOTSUP if GNUTLS is not enabled.
+ */
+int qcrypto_get_x509_hash_len(QCryptoHashAlgo alg);
+
+/**
+ * qcrypto_get_x509_keyid_len
+ * @flag: the key ID flag
+ *
+ * Determine the length of the key ID of the given @flag.
+ *
+ * Returns: the length on success,
+            0 on error,
+            -ENOTSUP if GNUTLS is not enabled.
+ */
+int qcrypto_get_x509_keyid_len(QCryptoKeyidFlags flag);
+
+/**
+ * qcrypto_get_x509_signature_algorithm
+ * @cert: pointer to the raw certiricate data
+ * @size: size of the certificate
+ * @errp: error pointer
+ *
+ * Determine the signature algorithm used to sign the @cert.
+ *
+ * Returns: a value from the QCryptoSigAlgo enum on success,
+ *          -1 on error,
+ *          -ENOTSUP if GNUTLS is not enabled.
+ */
+int qcrypto_get_x509_signature_algorithm(uint8_t *cert, size_t size, Error 
**errp);
+
 #endif
diff --git a/qapi/crypto.json b/qapi/crypto.json
index c9d967d782..af487dcecd 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -612,3 +612,83 @@
   'base': { 'alg': 'QCryptoAkCipherAlgo' },
   'discriminator': 'alg',
   'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }}
+
+##
+# @QCryptoKeyidFlags:
+#
+# The supported flags for the key ID
+#
+# @sha1: SHA-1
+#
+# @sha256: SHA-256
+#
+# @sha512: SHA-512
+#
+# @best-known: BEST-KNOWN
+#
+# Since: 10.1
+##
+{ 'enum': 'QCryptoKeyidFlags',
+  'data': ['sha1', 'sha256', 'sha512', 'best-known']}
+
+##
+# @QCryptoCertFmt:
+#
+# The supported certificate encoding formats
+#
+# @der: DER
+#
+# @pem: PEM
+#
+# Since: 10.1
+##
+{ 'enum': 'QCryptoCertFmt',
+  'data': ['der', 'pem']}
+
+##
+# @QCryptoSigAlgo:
+#
+# Algorithms for digital signature
+#
+# @unknown: UNKNOWN
+#
+# @rsa-sha1: RSA-SHA1 or RSA-SHA
+#
+# @dsa-sha1: DSA-SHA1 or DSA-SHA
+#
+# @rsa-md5: RSA-MD5
+#
+# @rsa-md2: RSA-MD2
+#
+# @rsa-rmd160: RSA-RMD160
+#
+# @rsa-sha256: RSA-SHA256
+#
+# @rsa-sha384: RSA-SHA384
+#
+# @rsa-sha512: RSA-SHA512
+#
+# @rsa-sha224: RSA-SHA224
+#
+# @dsa-sha224: DSA-SHA224
+#
+# @dsa-sha256: DSA-SHA256
+#
+# @ecdsa-sha1: ECDSA-SHA1
+#
+# @ecdsa-sha224: ECDSA-SHA224
+#
+# @ecdsa-sha256: ECDSA-SHA256
+#
+# @ecdsa-sha384: ECDSA-SHA384
+#
+# @ecdsa-sha512: ECDSA-SHA512
+#
+# Since: 10.1
+##
+{ 'enum': 'QCryptoSigAlgo',
+  'data': ['unknown', 'rsa-sha1', 'dsa-sha1',
+           'rsa-md5', 'rsa-md2', 'rsa-rmd160',
+           'rsa-sha256', 'rsa-sha384', 'rsa-sha512', 'rsa-sha224',
+           'dsa-sha224', 'dsa-sha256',
+           'ecdsa-sha1', 'ecdsa-sha224', 'ecdsa-sha256', 'ecdsa-sha384', 
'ecdsa-sha512']}
-- 
2.49.0


Reply via email to