Introduce new helper functions to extract certificate metadata:
qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
These functions provide support for metadata extraction and validity checking
for X.509 certificates.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
include/crypto/x509-utils.h | 73 +++++++++++
2 files changed, 321 insertions(+)
diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
index 2696d48155..f91fa56563 100644
--- a/crypto/x509-utils.c
+++ b/crypto/x509-utils.c
@@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
[QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
};
+static const int gnutls_to_qcrypto_pk_alg_map[] = {
+ [GNUTLS_PK_RSA] = QCRYPTO_PK_ALGO_RSA,
+ [GNUTLS_PK_DSA] = QCRYPTO_PK_ALGO_DSA,
+ [GNUTLS_PK_ECDSA] = QCRYPTO_PK_ALGO_ECDSA,
+ [GNUTLS_PK_RSA_OAEP] = QCRYPTO_PK_ALGO_RSA_OAEP,
+ [GNUTLS_PK_EDDSA_ED25519] = QCRYPTO_PK_ALGO_ED25519,
+ [GNUTLS_PK_EDDSA_ED448] = QCRYPTO_PK_ALGO_ED448,
+};
+
+static const int qcrypto_to_gnutls_keyid_flags_map[] = {
+ [QCRYPTO_HASH_ALGO_MD5] = -1,
+ [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_KEYID_USE_SHA1,
+ [QCRYPTO_HASH_ALGO_SHA224] = -1,
+ [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_KEYID_USE_SHA256,
+ [QCRYPTO_HASH_ALGO_SHA384] = -1,
+ [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_KEYID_USE_SHA512,
+ [QCRYPTO_HASH_ALGO_RIPEMD160] = -1,
+};
+
int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
QCryptoHashAlgo alg,
uint8_t *result,
@@ -121,6 +140,207 @@ cleanup:
return ret;
}
+int qcrypto_x509_check_cert_times(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};
+ time_t now = time(0);
+ time_t exp_time;
+ time_t act_time;
+
+ if (now == ((time_t)-1)) {
+ error_setg_errno(errp, errno, "Cannot get current time");
+ return ret;
+ }
+
+ 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;
+ }
+
+ exp_time = gnutls_x509_crt_get_expiration_time(crt);
+ if (exp_time == ((time_t)-1)) {
+ error_setg(errp, "Failed to get certificate expiration time");
+ goto cleanup;
+ }
+ if (exp_time < now) {
+ error_setg(errp, "The certificate has expired");
+ goto cleanup;
+ }
+
+ act_time = gnutls_x509_crt_get_activation_time(crt);
+ if (act_time == ((time_t)-1)) {
+ error_setg(errp, "Failed to get certificate activation time");
+ goto cleanup;
+ }
+ if (act_time > now) {
+ error_setg(errp, "The certificate is not yet active");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ gnutls_x509_crt_deinit(crt);
+ return ret;
+}
+
+int qcrypto_x509_get_pk_algorithm(uint8_t *cert, size_t size, Error **errp)
+{
+ int rc;
+ int ret = -1;
+ unsigned int bits;
+ 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;
+ }
+
+ rc = gnutls_x509_crt_get_pk_algorithm(crt, &bits);
+ if (rc >= G_N_ELEMENTS(gnutls_to_qcrypto_pk_alg_map) || rc < 0) {
+ error_setg(errp, "Unknown public key algorithm %d", rc);
+ goto cleanup;
+ }
+
+ ret = gnutls_to_qcrypto_pk_alg_map[rc];
+
+cleanup:
+ gnutls_x509_crt_deinit(crt);
+ return ret;
+}
+
+int qcrypto_x509_get_cert_key_id(uint8_t *cert, size_t size,
+ QCryptoHashAlgo hash_alg,
+ uint8_t **result,
+ size_t *resultlen,
+ Error **errp)
+{
+ int rc;
+ int ret = -1;
+ gnutls_x509_crt_t crt;
+ gnutls_datum_t datum = {.data = cert, .size = size};
+
+ if (hash_alg >= G_N_ELEMENTS(qcrypto_to_gnutls_hash_alg_map)) {
+ error_setg(errp, "Unknown hash algorithm %d", hash_alg);
+ return ret;
+ }
+
+ if (hash_alg >= G_N_ELEMENTS(qcrypto_to_gnutls_keyid_flags_map) ||
+ qcrypto_to_gnutls_keyid_flags_map[hash_alg] == -1) {
+ error_setg(errp, "Unsupported key id flag %d", hash_alg);
+ return ret;
+ }
+
+ 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;
+ }
+
+ *resultlen = gnutls_hash_get_len(qcrypto_to_gnutls_hash_alg_map[hash_alg]);
+ if (*resultlen == 0) {
+ error_setg(errp, "Failed to get hash algorithn length: %s", gnutls_strerror(rc));
+ goto cleanup;
+ }
+
+ *result = g_malloc0(*resultlen);
+ if (gnutls_x509_crt_get_key_id(crt,
+ qcrypto_to_gnutls_keyid_flags_map[hash_alg],
+ *result, resultlen) != 0) {
+ error_setg(errp, "Failed to get key ID from certificate");
+ g_clear_pointer(result, g_free);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ gnutls_x509_crt_deinit(crt);
+ return ret;
+}
+
+static int qcrypto_x509_get_ecc_curve(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};
+ gnutls_ecc_curve_t curve_id;
+ gnutls_datum_t x = {.data = NULL, .size = 0};
+ gnutls_datum_t y = {.data = NULL, .size = 0};
+
+ 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;
+ }
+
+ rc = gnutls_x509_crt_get_pk_ecc_raw(crt, &curve_id, &x, &y);
+ if (rc != 0) {
+ error_setg(errp, "Failed to get ECC public key curve: %s", gnutls_strerror(rc));
+ goto cleanup;
+ }
+
+ ret = curve_id;
+
+cleanup:
+ gnutls_x509_crt_deinit(crt);
+ gnutls_free(x.data);
+ gnutls_free(y.data);
+ return ret;
+}
+
+int qcrypto_x509_check_ecc_curve_p521(uint8_t *cert, size_t size, Error **errp)
+{
+ int curve_id;
+
+ curve_id = qcrypto_x509_get_ecc_curve(cert, size, errp);
+ if (curve_id == -1) {
+ return -1;
+ }
+
+ if (curve_id == GNUTLS_ECC_CURVE_INVALID) {
+ error_setg(errp, "Invalid ECC curve");
+ return -1;
+ }
+
+ if (curve_id == GNUTLS_ECC_CURVE_SECP521R1) {
+ return 1;
+ }
+
+ return 0;
+}
+
#else /* ! CONFIG_GNUTLS */
int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
@@ -142,4 +362,32 @@ int qcrypto_x509_convert_cert_der(uint8_t *cert, size_t size,
return -1;
}
+int qcrypto_x509_check_cert_times(uint8_t *cert, size_t size, Error **errp)
+{
+ error_setg(errp, "GNUTLS is required to get certificate times");
+ return -1;
+}
+
+int qcrypto_x509_get_pk_algorithm(uint8_t *cert, size_t size, Error **errp)
+{
+ error_setg(errp, "GNUTLS is required to get public key algorithm");
+ return -1;
+}
+
+int qcrypto_x509_get_cert_key_id(uint8_t *cert, size_t size,
+ QCryptoHashAlgo hash_alg,
+ uint8_t **result,
+ size_t *resultlen,
+ Error **errp)
+{
+ error_setg(errp, "GNUTLS is required to get key ID");
+ return -1;
+}
+
+int qcrypto_x509_check_ecc_curve_p521(uint8_t *cert, size_t size, Error **errp)
+{
+ error_setg(errp, "GNUTLS is required to determine ecc curve");
+ return -1;
+}
+
#endif /* ! CONFIG_GNUTLS */
diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h
index 91ae79fb03..f65be67a2c 100644
--- a/include/crypto/x509-utils.h
+++ b/include/crypto/x509-utils.h
@@ -13,6 +13,15 @@
#include "crypto/hash.h"
+typedef enum {
+ QCRYPTO_PK_ALGO_RSA,
+ QCRYPTO_PK_ALGO_DSA,
+ QCRYPTO_PK_ALGO_ECDSA,
+ QCRYPTO_PK_ALGO_RSA_OAEP,
+ QCRYPTO_PK_ALGO_ED25519,
+ QCRYPTO_PK_ALGO_ED448,
+} QCryptoPkAlgo;
+
int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
QCryptoHashAlgo hash,
uint8_t *result,
@@ -40,4 +49,68 @@ int qcrypto_x509_convert_cert_der(uint8_t *cert, size_t size,
size_t *resultlen,
Error **errp);
+/**
+ * qcrypto_x509_check_cert_times
+ * @cert: pointer to the raw certificate data
+ * @size: size of the certificate
+ * @errp: error pointer
+ *
+ * Check whether the activation and expiration times of @cert
+ * are valid at the current time.
+ *
+ * Returns: 0 if the certificate times are valid,
+ * -1 on error.
+ */
+int qcrypto_x509_check_cert_times(uint8_t *cert, size_t size, Error **errp);
+
+/**
+ * qcrypto_x509_get_pk_algorithm
+ * @cert: pointer to the raw certificate data
+ * @size: size of the certificate
+ * @errp: error pointer
+ *
+ * Determine the public key algorithm of the @cert.
+ *
+ * Returns: a value from the QCryptoPkAlgo enum on success,
+ * -1 on error.
+ */
+int qcrypto_x509_get_pk_algorithm(uint8_t *cert, size_t size, Error **errp);
+
+/**
+ * qcrypto_x509_get_cert_key_id
+ * @cert: pointer to the raw certificate data
+ * @size: size of the certificate
+ * @hash_alg: the hash algorithm flag
+ * @result: output location for the allocated buffer for key ID
+ * (the function allocates memory which must be freed by the caller)
+ * @resultlen: pointer to the size of the buffer
+ * (will be updated with the actual size of key id)
+ * @errp: error pointer
+ *
+ * Retrieve the key ID from the @cert based on the specified @flag.
+ *
+ * Returns: 0 if key ID was successfully stored in @result,
+ * -1 on error.
+ */
+int qcrypto_x509_get_cert_key_id(uint8_t *cert, size_t size,
+ QCryptoHashAlgo hash_alg,
+ uint8_t **result,
+ size_t *resultlen,
+ Error **errp);
+
+/**
+ * qcrypto_x509_check_ecc_curve_p521
+ * @cert: pointer to the raw certificate data
+ * @size: size of the certificate
+ * @errp: error pointer
+ *
+ * Determine whether the ECC public key in the given certificate uses the P-521
+ * curve.
+ *
+ * Returns: 0 if ECC public key does not use P521 curve.
+ * 1 if ECC public key uses P521 curve.
+ * -1 on error.
+ */
+int qcrypto_x509_check_ecc_curve_p521(uint8_t *cert, size_t size, Error **errp);
+
#endif
--
2.51.1
On 08/12/2025 22.32, Zhuoying Cai wrote:
> Introduce new helper functions to extract certificate metadata:
>
> qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
> qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
> qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
> qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
>
> These functions provide support for metadata extraction and validity checking
> for X.509 certificates.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
> include/crypto/x509-utils.h | 73 +++++++++++
> 2 files changed, 321 insertions(+)
>
> diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
> index 2696d48155..f91fa56563 100644
> --- a/crypto/x509-utils.c
> +++ b/crypto/x509-utils.c
> @@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
> [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
> };
>
> +static const int gnutls_to_qcrypto_pk_alg_map[] = {
Could you do me a favour and add a line like this at the beginning of this
array:
[GNUTLS_PK_UNKNOWN] = QCRYPTO_PK_ALGO_UNKNOWN,
and then also set QCRYPTO_PK_ALGO_UNKNOWN = 0 in the enum in the header?
That way we can be sure that zero values are not accidentally mapped to a
valid algorithm.
> + [GNUTLS_PK_RSA] = QCRYPTO_PK_ALGO_RSA,
> + [GNUTLS_PK_DSA] = QCRYPTO_PK_ALGO_DSA,
> + [GNUTLS_PK_ECDSA] = QCRYPTO_PK_ALGO_ECDSA,
> + [GNUTLS_PK_RSA_OAEP] = QCRYPTO_PK_ALGO_RSA_OAEP,
> + [GNUTLS_PK_EDDSA_ED25519] = QCRYPTO_PK_ALGO_ED25519,
> + [GNUTLS_PK_EDDSA_ED448] = QCRYPTO_PK_ALGO_ED448,
> +};
> +
> +static const int qcrypto_to_gnutls_keyid_flags_map[] = {
> + [QCRYPTO_HASH_ALGO_MD5] = -1,
> + [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_KEYID_USE_SHA1,
> + [QCRYPTO_HASH_ALGO_SHA224] = -1,
> + [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_KEYID_USE_SHA256,
> + [QCRYPTO_HASH_ALGO_SHA384] = -1,
> + [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_KEYID_USE_SHA512,
> + [QCRYPTO_HASH_ALGO_RIPEMD160] = -1,
> +};
> +
> int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
> QCryptoHashAlgo alg,
> uint8_t *result,
> @@ -121,6 +140,207 @@ cleanup:
> return ret;
> }
>
> +int qcrypto_x509_check_cert_times(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};
> + time_t now = time(0);
time() takes a pointer, so "time(NULL)" would be more appropriate here.
> + time_t exp_time;
> + time_t act_time;
> +
> + if (now == ((time_t)-1)) {
> + error_setg_errno(errp, errno, "Cannot get current time");
> + return ret;
> + }
> +
> + 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;
> + }
> +
> + exp_time = gnutls_x509_crt_get_expiration_time(crt);
> + if (exp_time == ((time_t)-1)) {
> + error_setg(errp, "Failed to get certificate expiration time");
> + goto cleanup;
> + }
> + if (exp_time < now) {
> + error_setg(errp, "The certificate has expired");
> + goto cleanup;
> + }
> +
> + act_time = gnutls_x509_crt_get_activation_time(crt);
> + if (act_time == ((time_t)-1)) {
> + error_setg(errp, "Failed to get certificate activation time");
> + goto cleanup;
> + }
> + if (act_time > now) {
> + error_setg(errp, "The certificate is not yet active");
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + gnutls_x509_crt_deinit(crt);
> + return ret;
> +}
> +
> +int qcrypto_x509_get_pk_algorithm(uint8_t *cert, size_t size, Error **errp)
> +{
> + int rc;
> + int ret = -1;
> + unsigned int bits;
> + 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;
> + }
> +
> + rc = gnutls_x509_crt_get_pk_algorithm(crt, &bits);
> + if (rc >= G_N_ELEMENTS(gnutls_to_qcrypto_pk_alg_map) || rc < 0) {
> + error_setg(errp, "Unknown public key algorithm %d", rc);
> + goto cleanup;
> + }
> +
> + ret = gnutls_to_qcrypto_pk_alg_map[rc];
> +
> +cleanup:
> + gnutls_x509_crt_deinit(crt);
> + return ret;
> +}
> +
> +int qcrypto_x509_get_cert_key_id(uint8_t *cert, size_t size,
> + QCryptoHashAlgo hash_alg,
> + uint8_t **result,
> + size_t *resultlen,
> + Error **errp)
> +{
> + int rc;
> + int ret = -1;
> + gnutls_x509_crt_t crt;
> + gnutls_datum_t datum = {.data = cert, .size = size};
> +
> + if (hash_alg >= G_N_ELEMENTS(qcrypto_to_gnutls_hash_alg_map)) {
> + error_setg(errp, "Unknown hash algorithm %d", hash_alg);
> + return ret;
> + }
> +
> + if (hash_alg >= G_N_ELEMENTS(qcrypto_to_gnutls_keyid_flags_map) ||
> + qcrypto_to_gnutls_keyid_flags_map[hash_alg] == -1) {
> + error_setg(errp, "Unsupported key id flag %d", hash_alg);
> + return ret;
> + }
> +
> + 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;
> + }
> +
> + *resultlen = gnutls_hash_get_len(qcrypto_to_gnutls_hash_alg_map[hash_alg]);
> + if (*resultlen == 0) {
> + error_setg(errp, "Failed to get hash algorithn length: %s", gnutls_strerror(rc));
> + goto cleanup;
> + }
> +
> + *result = g_malloc0(*resultlen);
> + if (gnutls_x509_crt_get_key_id(crt,
> + qcrypto_to_gnutls_keyid_flags_map[hash_alg],
> + *result, resultlen) != 0) {
> + error_setg(errp, "Failed to get key ID from certificate");
> + g_clear_pointer(result, g_free);
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + gnutls_x509_crt_deinit(crt);
> + return ret;
> +}
> +
> +static int qcrypto_x509_get_ecc_curve(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};
> + gnutls_ecc_curve_t curve_id;
> + gnutls_datum_t x = {.data = NULL, .size = 0};
> + gnutls_datum_t y = {.data = NULL, .size = 0};
> +
> + 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;
> + }
> +
> + rc = gnutls_x509_crt_get_pk_ecc_raw(crt, &curve_id, &x, &y);
> + if (rc != 0) {
> + error_setg(errp, "Failed to get ECC public key curve: %s", gnutls_strerror(rc));
> + goto cleanup;
> + }
> +
> + ret = curve_id;
> +
> +cleanup:
> + gnutls_x509_crt_deinit(crt);
> + gnutls_free(x.data);
> + gnutls_free(y.data);
> + return ret;
> +}
> +
> +int qcrypto_x509_check_ecc_curve_p521(uint8_t *cert, size_t size, Error **errp)
> +{
> + int curve_id;
> +
> + curve_id = qcrypto_x509_get_ecc_curve(cert, size, errp);
> + if (curve_id == -1) {
> + return -1;
> + }
> +
> + if (curve_id == GNUTLS_ECC_CURVE_INVALID) {
> + error_setg(errp, "Invalid ECC curve");
> + return -1;
> + }
> +
> + if (curve_id == GNUTLS_ECC_CURVE_SECP521R1) {
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> #else /* ! CONFIG_GNUTLS */
>
> int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size,
> @@ -142,4 +362,32 @@ int qcrypto_x509_convert_cert_der(uint8_t *cert, size_t size,
> return -1;
> }
>
> +int qcrypto_x509_check_cert_times(uint8_t *cert, size_t size, Error **errp)
> +{
> + error_setg(errp, "GNUTLS is required to get certificate times");
> + return -1;
> +}
> +
> +int qcrypto_x509_get_pk_algorithm(uint8_t *cert, size_t size, Error **errp)
> +{
> + error_setg(errp, "GNUTLS is required to get public key algorithm");
> + return -1;
> +}
> +
> +int qcrypto_x509_get_cert_key_id(uint8_t *cert, size_t size,
> + QCryptoHashAlgo hash_alg,
> + uint8_t **result,
> + size_t *resultlen,
> + Error **errp)
> +{
> + error_setg(errp, "GNUTLS is required to get key ID");
> + return -1;
> +}
> +
> +int qcrypto_x509_check_ecc_curve_p521(uint8_t *cert, size_t size, Error **errp)
> +{
> + error_setg(errp, "GNUTLS is required to determine ecc curve");
> + return -1;
> +}
> +
> #endif /* ! CONFIG_GNUTLS */
> diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h
> index 91ae79fb03..f65be67a2c 100644
> --- a/include/crypto/x509-utils.h
> +++ b/include/crypto/x509-utils.h
> @@ -13,6 +13,15 @@
>
> #include "crypto/hash.h"
>
> +typedef enum {
Please add QCRYPTO_PK_ALGO_UNKNOWN here (or maybe rather
QCRYPTO_PK_ALGO_INVALID ?)
> + QCRYPTO_PK_ALGO_RSA,
> + QCRYPTO_PK_ALGO_DSA,
> + QCRYPTO_PK_ALGO_ECDSA,
> + QCRYPTO_PK_ALGO_RSA_OAEP,
> + QCRYPTO_PK_ALGO_ED25519,
> + QCRYPTO_PK_ALGO_ED448,
> +} QCryptoPkAlgo;
Thanks,
Thomas
On Fri, Jan 09, 2026 at 12:06:25PM +0100, Thomas Huth wrote:
> On 08/12/2025 22.32, Zhuoying Cai wrote:
> > Introduce new helper functions to extract certificate metadata:
> >
> > qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
> > qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
> > qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
> > qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
> >
> > These functions provide support for metadata extraction and validity checking
> > for X.509 certificates.
> >
> > Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> > ---
> > crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
> > include/crypto/x509-utils.h | 73 +++++++++++
> > 2 files changed, 321 insertions(+)
> >
> > diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
> > index 2696d48155..f91fa56563 100644
> > --- a/crypto/x509-utils.c
> > +++ b/crypto/x509-utils.c
> > @@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
> > [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
> > };
> > +static const int gnutls_to_qcrypto_pk_alg_map[] = {
>
> Could you do me a favour and add a line like this at the beginning of this
> array:
>
> [GNUTLS_PK_UNKNOWN] = QCRYPTO_PK_ALGO_UNKNOWN,
>
> and then also set QCRYPTO_PK_ALGO_UNKNOWN = 0 in the enum in the header?
> That way we can be sure that zero values are not accidentally mapped to a
> valid algorithm.
That would be special casing just one enum type in the crypto subsystem
to have this special unknown value. We've got 1000's of enums across
QEMU and don't generally add such a special zeroth constant, so I don't
find this suggested change to be desirable.
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 :|
On 09/01/2026 12.13, Daniel P. Berrangé wrote:
> On Fri, Jan 09, 2026 at 12:06:25PM +0100, Thomas Huth wrote:
>> On 08/12/2025 22.32, Zhuoying Cai wrote:
>>> Introduce new helper functions to extract certificate metadata:
>>>
>>> qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
>>> qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
>>> qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
>>> qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
>>>
>>> These functions provide support for metadata extraction and validity checking
>>> for X.509 certificates.
>>>
>>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>>> ---
>>> crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
>>> include/crypto/x509-utils.h | 73 +++++++++++
>>> 2 files changed, 321 insertions(+)
>>>
>>> diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
>>> index 2696d48155..f91fa56563 100644
>>> --- a/crypto/x509-utils.c
>>> +++ b/crypto/x509-utils.c
>>> @@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
>>> [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
>>> };
>>> +static const int gnutls_to_qcrypto_pk_alg_map[] = {
>>
>> Could you do me a favour and add a line like this at the beginning of this
>> array:
>>
>> [GNUTLS_PK_UNKNOWN] = QCRYPTO_PK_ALGO_UNKNOWN,
>>
>> and then also set QCRYPTO_PK_ALGO_UNKNOWN = 0 in the enum in the header?
>> That way we can be sure that zero values are not accidentally mapped to a
>> valid algorithm.
>
> That would be special casing just one enum type in the crypto subsystem
> to have this special unknown value. We've got 1000's of enums across
> QEMU and don't generally add such a special zeroth constant, so I don't
> find this suggested change to be desirable.
I came up with this idea when reviewing the qcrypto_x509_get_pk_algorithm()
function in this patch which is basically doing:
ret = gnutls_to_qcrypto_pk_alg_map[rc];
My concern is that if someone ever extends the QCryptoPkAlgo in the header,
but forgets to fill in as much entries to gnutls_to_qcrypto_pk_alg_map as
there are entries in the enum, we're in trouble, since the wholes in the
array will be padded with zeros that then get mapped to QCRYPTO_PK_ALGO_RSA.
Having a clearly invalid meaning of 0 would help in such cases.
But if you consider this as too unlikely to happen, then never mind my
suggestion.
Thomas
On Fri, Jan 09, 2026 at 12:24:32PM +0100, Thomas Huth wrote:
> On 09/01/2026 12.13, Daniel P. Berrangé wrote:
> > On Fri, Jan 09, 2026 at 12:06:25PM +0100, Thomas Huth wrote:
> > > On 08/12/2025 22.32, Zhuoying Cai wrote:
> > > > Introduce new helper functions to extract certificate metadata:
> > > >
> > > > qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
> > > > qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
> > > > qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
> > > > qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
> > > >
> > > > These functions provide support for metadata extraction and validity checking
> > > > for X.509 certificates.
> > > >
> > > > Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> > > > ---
> > > > crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
> > > > include/crypto/x509-utils.h | 73 +++++++++++
> > > > 2 files changed, 321 insertions(+)
> > > >
> > > > diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
> > > > index 2696d48155..f91fa56563 100644
> > > > --- a/crypto/x509-utils.c
> > > > +++ b/crypto/x509-utils.c
> > > > @@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
> > > > [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
> > > > };
> > > > +static const int gnutls_to_qcrypto_pk_alg_map[] = {
> > >
> > > Could you do me a favour and add a line like this at the beginning of this
> > > array:
> > >
> > > [GNUTLS_PK_UNKNOWN] = QCRYPTO_PK_ALGO_UNKNOWN,
> > >
> > > and then also set QCRYPTO_PK_ALGO_UNKNOWN = 0 in the enum in the header?
> > > That way we can be sure that zero values are not accidentally mapped to a
> > > valid algorithm.
> >
> > That would be special casing just one enum type in the crypto subsystem
> > to have this special unknown value. We've got 1000's of enums across
> > QEMU and don't generally add such a special zeroth constant, so I don't
> > find this suggested change to be desirable.
>
> I came up with this idea when reviewing the qcrypto_x509_get_pk_algorithm()
> function in this patch which is basically doing:
>
> ret = gnutls_to_qcrypto_pk_alg_map[rc];
>
> My concern is that if someone ever extends the QCryptoPkAlgo in the header,
> but forgets to fill in as much entries to gnutls_to_qcrypto_pk_alg_map as
> there are entries in the enum, we're in trouble, since the wholes in the
> array will be padded with zeros that then get mapped to QCRYPTO_PK_ALGO_RSA.
> Having a clearly invalid meaning of 0 would help in such cases.
Hmm, looking at the usage of qcrypto_x509_get_pk_algorithm(), I think
the method and the QCryptoPkAlgo enums should be deleted entirely.
In the next patch, the caller does
/* public key algorithm */
algo = qcrypto_x509_get_pk_algorithm(cert.raw, cert.size, &err);
if (algo < 0) {
error_report_err(err);
return -1;
}
if (algo == QCRYPTO_PK_ALGO_ECDSA) {
rc = qcrypto_x509_check_ecc_curve_p521(cert.raw, cert.size, &err);
if (rc == -1) {
error_report_err(err);
return -1;
}
return (rc == 1) ? DIAG_320_VCE_KEYTYPE_ECDSA_P521 :
DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
}
return DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
so the caller doesn't actually care about anything other than
PK_ALGO_ECDSA, so IMHO going to the trouble of defining an enum
and mapping gnutls enums is pointless.
This caller should unconditionally call
qcrypto_x509_check_ecc_curve_p521()
and then then qcrypto_x509_check_ecc_curve_p521() method should
check whether the pk algorithm is GNUTLS_PK_ECDSA internally
and just return 0 if not.
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 :|
On 1/9/26 6:38 AM, Daniel P. Berrangé wrote:
> On Fri, Jan 09, 2026 at 12:24:32PM +0100, Thomas Huth wrote:
>> On 09/01/2026 12.13, Daniel P. Berrangé wrote:
>>> On Fri, Jan 09, 2026 at 12:06:25PM +0100, Thomas Huth wrote:
>>>> On 08/12/2025 22.32, Zhuoying Cai wrote:
>>>>> Introduce new helper functions to extract certificate metadata:
>>>>>
>>>>> qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time
>>>>> qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate
>>>>> qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate
>>>>> qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve
>>>>>
>>>>> These functions provide support for metadata extraction and validity checking
>>>>> for X.509 certificates.
>>>>>
>>>>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>>>>> ---
>>>>> crypto/x509-utils.c | 248 ++++++++++++++++++++++++++++++++++++
>>>>> include/crypto/x509-utils.h | 73 +++++++++++
>>>>> 2 files changed, 321 insertions(+)
>>>>>
>>>>> diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c
>>>>> index 2696d48155..f91fa56563 100644
>>>>> --- a/crypto/x509-utils.c
>>>>> +++ b/crypto/x509-utils.c
>>>>> @@ -27,6 +27,25 @@ static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = {
>>>>> [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160,
>>>>> };
>>>>> +static const int gnutls_to_qcrypto_pk_alg_map[] = {
>>>>
>>>> Could you do me a favour and add a line like this at the beginning of this
>>>> array:
>>>>
>>>> [GNUTLS_PK_UNKNOWN] = QCRYPTO_PK_ALGO_UNKNOWN,
>>>>
>>>> and then also set QCRYPTO_PK_ALGO_UNKNOWN = 0 in the enum in the header?
>>>> That way we can be sure that zero values are not accidentally mapped to a
>>>> valid algorithm.
>>>
>>> That would be special casing just one enum type in the crypto subsystem
>>> to have this special unknown value. We've got 1000's of enums across
>>> QEMU and don't generally add such a special zeroth constant, so I don't
>>> find this suggested change to be desirable.
>>
>> I came up with this idea when reviewing the qcrypto_x509_get_pk_algorithm()
>> function in this patch which is basically doing:
>>
>> ret = gnutls_to_qcrypto_pk_alg_map[rc];
>>
>> My concern is that if someone ever extends the QCryptoPkAlgo in the header,
>> but forgets to fill in as much entries to gnutls_to_qcrypto_pk_alg_map as
>> there are entries in the enum, we're in trouble, since the wholes in the
>> array will be padded with zeros that then get mapped to QCRYPTO_PK_ALGO_RSA.
>> Having a clearly invalid meaning of 0 would help in such cases.
>
> Hmm, looking at the usage of qcrypto_x509_get_pk_algorithm(), I think
> the method and the QCryptoPkAlgo enums should be deleted entirely.
>
> In the next patch, the caller does
>
>
> /* public key algorithm */
> algo = qcrypto_x509_get_pk_algorithm(cert.raw, cert.size, &err);
> if (algo < 0) {
> error_report_err(err);
> return -1;
> }
>
> if (algo == QCRYPTO_PK_ALGO_ECDSA) {
> rc = qcrypto_x509_check_ecc_curve_p521(cert.raw, cert.size, &err);
> if (rc == -1) {
> error_report_err(err);
> return -1;
> }
>
> return (rc == 1) ? DIAG_320_VCE_KEYTYPE_ECDSA_P521 :
> DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
> }
>
> return DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
>
>
> so the caller doesn't actually care about anything other than
> PK_ALGO_ECDSA, so IMHO going to the trouble of defining an enum
> and mapping gnutls enums is pointless.
>
> This caller should unconditionally call
>
> qcrypto_x509_check_ecc_curve_p521()
>
> and then then qcrypto_x509_check_ecc_curve_p521() method should
> check whether the pk algorithm is GNUTLS_PK_ECDSA internally
> and just return 0 if not.
>
>
> With regards,
> Daniel
Thanks for the comments. I agree with your suggestion. Given the
current usage, the caller only needs to know whether the key is ECDSA
P-521 or not.
I had a similar concern to Thomas about the QCryptoPkAlgo enum and its
mapping, since the gnutls_pk_algorithm_t enum includes many values.
Moving the algorithm check into qcrypto_x509_check_ecc_curve_p521()
addresses this. I'll rework the patch accordingly.
Thanks again for all the helpful feedback!
On 12/8/2025 1:32 PM, Zhuoying Cai wrote: > Introduce new helper functions to extract certificate metadata: > > qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time > qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate > qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate > qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve I think this function name has changed to qcrypto_x509_check_ecc_curve_p521? > These functions provide support for metadata extraction and validity checking > for X.509 certificates. > > Signed-off-by: Zhuoying Cai<zycai@linux.ibm.com> > --- The patch LGTM Reviewed-by: Farhan Ali<alifm@linux.ibm.com>
On 1/8/26 2:32 PM, Farhan Ali wrote: > > On 12/8/2025 1:32 PM, Zhuoying Cai wrote: >> Introduce new helper functions to extract certificate metadata: >> >> qcrypto_x509_check_cert_times() - validates the certificate's validity period against the current time >> qcrypto_x509_get_pk_algorithm() - returns the public key algorithm used in the certificate >> qcrypto_x509_get_cert_key_id() - extracts the key ID from the certificate >> qcrypto_x509_is_ecc_curve_p521() - determines the ECC public key algorithm uses P-521 curve > > I think this function name has changed to qcrypto_x509_check_ecc_curve_p521? > Thanks for pointing it out! I'll fix it in the next version. >> These functions provide support for metadata extraction and validity checking >> for X.509 certificates. >> >> Signed-off-by: Zhuoying Cai<zycai@linux.ibm.com> >> --- > > The patch LGTM > > Reviewed-by: Farhan Ali<alifm@linux.ibm.com> > >
© 2016 - 2026 Red Hat, Inc.