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