Introduce new helper functions for x509 certificate, which will be used by the certificate store:
qcrypto_x509_convert_cert_der() - converts a certificate from PEM to DER format qcrypto_x509_get_keyid_len() - returns the length of the key ID qcrypto_x509_get_signature_algorithm() - returns signature algorithm of the certificate These functions provide support for certificate format conversion and metadata extraction. Signed-off-by: Zhuoying Cai <zy...@linux.ibm.com> --- crypto/meson.build | 5 +- crypto/x509-utils.c | 155 ++++++++++++++++++++++++++++++++++++ include/crypto/x509-utils.h | 71 +++++++++++++++++ 3 files changed, 227 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..d2cf790d5b 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,87 @@ 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_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 gnutls_to_qcrypto_sig_alg_map[] = { + [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_x509_convert_cert_der(uint8_t *cert, size_t size, + uint8_t **result, size_t *resultlen, + Error **errp) +{ + int ret = -1; + int rc; + gnutls_x509_crt_t crt; + gnutls_datum_t datum = {.data = cert, .size = size}; + + rc = gnutls_x509_crt_init(&crt); + if (rc < 0) { + error_setg(errp, "Failed to initialize certificate: %s", gnutls_strerror(rc)); + return ret; + } + + rc = gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM); + if (rc != 0) { + error_setg(errp, "Failed to import certificate: %s", gnutls_strerror(rc)); + goto cleanup; + } + + *result = g_malloc0(*resultlen); + rc = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_DER, *result, resultlen); + if (rc != 0) { + error_setg(errp, "Failed to convert certificate to DER format: %s", + gnutls_strerror(rc)); + goto cleanup; + } + + ret = 0; + +cleanup: + gnutls_x509_crt_deinit(crt); + return ret; +} + +int qcrypto_x509_get_keyid_len(QCryptoKeyidFlags flag, Error **errp) +{ + if (flag >= G_N_ELEMENTS(qcrypto_to_gnutls_keyid_flags_map)) { + error_setg(errp, "Unknow key ID flag %d", flag); + return -1; + } + + if ((flag & qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_SHA512]) || + (flag & qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_BEST_KNOWN])) { + return QCRYPTO_HASH_DIGEST_LEN_SHA512; + } else if (flag & qcrypto_to_gnutls_keyid_flags_map[QCRYPTO_KEYID_FLAGS_SHA256]) { + return QCRYPTO_HASH_DIGEST_LEN_SHA256; + } else { + return QCRYPTO_HASH_DIGEST_LEN_SHA1; + } +} + int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, QCryptoHashAlgo alg, uint8_t *result, @@ -74,3 +157,75 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, gnutls_x509_crt_deinit(crt); return ret; } + +int qcrypto_x509_get_signature_algorithm(uint8_t *cert, size_t size, Error **errp) +{ + int rc; + int ret = -1; + gnutls_x509_crt_t crt; + gnutls_datum_t datum = {.data = cert, .size = size}; + + rc = gnutls_x509_crt_init(&crt); + if (rc < 0) { + error_setg(errp, "Failed to initialize certificate: %s", gnutls_strerror(rc)); + return ret; + } + + rc = gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM); + if (rc != 0) { + error_setg(errp, "Failed to import certificate: %s", gnutls_strerror(rc)); + goto cleanup; + } + + /* + * This function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms + * are mapped to GNUTLS_SIGN_UNKNOWN. + */ + rc = gnutls_x509_crt_get_signature_algorithm(crt); + if (rc >= G_N_ELEMENTS(gnutls_to_qcrypto_sig_alg_map)) { + error_setg(errp, "Unknown signature algorithm %d", rc); + goto cleanup; + } + + ret = gnutls_to_qcrypto_sig_alg_map[rc]; + +cleanup: + gnutls_x509_crt_deinit(crt); + return ret; +} + +#else /* ! CONFIG_GNUTLS */ + +int qcrypto_x509_convert_cert_der(uint8_t *cert, size_t size, + uint8_t **result, + size_t *resultlen, + Error **errp) +{ + error_setg(errp, "GNUTLS is required to export X.509 certificate"); + return -1; +} + +int qcrypto_x509_get_keyid_len(QCryptoKeyidFlags flag, Error **errp) +{ + error_setg(errp, "GNUTLS is required to get key ID length"); + return -1; +} + +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 -1; +} + +int qcrypto_x509_get_signature_algorithm(uint8_t *cert, size_t size, Error **errp) +{ + error_setg(errp, "GNUTLS is required to get signature algorithm"); + return -1; +} + +#endif /* ! CONFIG_GNUTLS */ diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h index 1e99661a71..d916d248bb 100644 --- a/include/crypto/x509-utils.h +++ b/include/crypto/x509-utils.h @@ -13,10 +13,81 @@ #include "crypto/hash.h" +typedef enum { + QCRYPTO_KEYID_FLAGS_SHA1, + QCRYPTO_KEYID_FLAGS_SHA256, + QCRYPTO_KEYID_FLAGS_SHA512, + QCRYPTO_KEYID_FLAGS_BEST_KNOWN, +} QCryptoKeyidFlags; + +typedef enum { + QCRYPTO_SIG_ALGO_UNKNOWN, + QCRYPTO_SIG_ALGO_RSA_SHA1, + QCRYPTO_SIG_ALGO_DSA_SHA1, + QCRYPTO_SIG_ALGO_RSA_MD5, + QCRYPTO_SIG_ALGO_RSA_MD2, + QCRYPTO_SIG_ALGO_RSA_RMD160, + QCRYPTO_SIG_ALGO_RSA_SHA256, + QCRYPTO_SIG_ALGO_RSA_SHA384, + QCRYPTO_SIG_ALGO_RSA_SHA512, + QCRYPTO_SIG_ALGO_RSA_SHA224, + QCRYPTO_SIG_ALGO_DSA_SHA224, + QCRYPTO_SIG_ALGO_DSA_SHA256, + QCRYPTO_SIG_ALGO_ECDSA_SHA1, + QCRYPTO_SIG_ALGO_ECDSA_SHA224, + QCRYPTO_SIG_ALGO_ECDSA_SHA256, + QCRYPTO_SIG_ALGO_ECDSA_SHA384, + QCRYPTO_SIG_ALGO_ECDSA_SHA512, +} QCryptoSigAlgo; + int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, QCryptoHashAlgo hash, uint8_t *result, size_t *resultlen, Error **errp); +/** + * qcrypto_x509_convert_cert_der + * @cert: pointer to the raw certificate data in PEM format + * @size: size of the certificate + * @result: output location for the allocated buffer for the certificate in DER format + (the function allocates memory which must be freed by the caller) + * @resultlen: pointer to the size of the buffer + (will be replaced by the actual size of the DER-encoded certificate) + * @errp: error pointer + * + * Convert given @cert from PEM to DER format. + * + * Returns: 0 on success, + * -1 on error. + */ +int qcrypto_x509_convert_cert_der(uint8_t *cert, size_t size, + uint8_t **result, + size_t *resultlen, + Error **errp); + +/** + * qcrypto_x509_get_keyid_len + * @flag: the key ID flag + * + * Determine the length of the key ID of the given @flag. + * + * Returns: the length on success, + * -1 on error. + */ +int qcrypto_x509_get_keyid_len(QCryptoKeyidFlags flag, Error **errp); + +/** + * qcrypto_x509_get_signature_algorithm + * @cert: pointer to the raw certificate 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. + */ +int qcrypto_x509_get_signature_algorithm(uint8_t *cert, size_t size, Error **errp); + #endif -- 2.49.0