From 55fbbaf27efa6e42f0038f858d76e213da7d27e1 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 1/12] Replace deprecated OpenSSL 3.0.0 function calls Use EVP_PKEY instead of algorithm-specific calls to generate required parameters and keys. This is a prerequisite for supporting PQ algorithms in a clean way and will future-proof certmonger. While not strictly related to OSSL 3.0.0 deprecation, add a global error logging function so we can reduce code duplication and make it easier to log what happened when a call fails. This was helpful during development of this change. Fixes: https://pagure.io/certmonger/issue/292 Signed-off-by: Rob Crittenden --- diff --git a/src/Makefile.am b/src/Makefile.am index b20cb43..ef3b743 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -196,7 +196,8 @@ if WITH_XMLRPC noinst_PROGRAMS += submit-x endif tdbusm_check_SOURCES = tdbusm-check.c tm.c tm.h -tdbusm_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(POPT_LIBS) $(LDAP_LIBS) +tdbusm_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(POPT_LIBS) $(LDAP_LIBS) \ + $(OPENSSL_LIBS) $(NSS_LIBS) serial_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(LTLIBICONV) $(LDAP_LIBS) nl_check_LDADD = libcm.a $(CERTMONGER_LIBS) $(LDAP_LIBS) if WITH_XMLRPC @@ -230,7 +231,7 @@ ipa_submit_SOURCES = ipa.c srvloc.c srvloc.h store.h store-gen.c \ ipa_submit_LDADD = $(XMLRPC_LIBS) $(LDAP_LIBS) $(KRB5_LIBS) $(TALLOC_LIBS) \ $(GMP_LIBS) $(IDN_LIBS) $(OPENSSL_LIBS) $(UUID_LIBS) \ $(RESOLV_LIBS) $(LTLIBICONV) $(POPT_LIBS) $(CURL_LIBS) \ - $(JANSSON_LIBS) + $(JANSSON_LIBS) $(OPENSSL_LIBS) $(NSS_LIBS) if WITH_XMLRPC certmaster_submit_CFLAGS = $(AM_CFLAGS) $(NSS_CFLAGS) certmaster_submit_SOURCES = certmaster.c submit-x.c submit-x.h \ @@ -285,17 +286,18 @@ local_submit_SOURCES = local.c env.h store.h store-gen.c submit-o.c submit-o.h \ util.c util.h util-o.c util-o.h pin.c pin.h local_submit_LDADD = $(NSS_LIBS) $(OPENSSL_LIBS) $(TALLOC_LIBS) $(UUID_LIBS) \ $(POPT_LIBS) -submit_d_CFLAGS = $(AM_CFLAGS) $(CURL_CFLAGS) $(XML_CFLAGS) -DCM_SUBMIT_D_MAIN +submit_d_CFLAGS = $(AM_CFLAGS) $(CURL_CFLAGS) $(XML_CFLAGS) $(OPENSSL_CFLAGS) \ + $(NSS_CFLAGS) -DCM_SUBMIT_D_MAIN submit_d_SOURCES = submit-d.c submit-d.h submit-h.c submit-h.h \ submit-u.c submit-u.h log.c log.h tm.c tm.h util-m.c util-m.h submit_d_LDADD = libcm-o.a $(CURL_LIBS) $(OPENSSL_LIBS) $(XML_LIBS) \ $(TALLOC_LIBS) $(GMP_LIBS) $(UUID_LIBS) $(LTLIBICONV) \ - $(POPT_LIBS) $(JANSSON_LIBS) -submit_h_CFLAGS = $(AM_CFLAGS) $(CURL_CFLAGS) $(XML_CFLAGS) \ - $(JANSSON_CFLAGS) -DCM_SUBMIT_H_MAIN + $(POPT_LIBS) $(JANSSON_LIBS) $(OPENSSL_LIBS) $(NSS_LIBS) +submit_h_CFLAGS = $(AM_CFLAGS) $(NSS_CFLAGS) $(CURL_CFLAGS) $(XML_CFLAGS) \ + $(JANSSON_CFLAGS) -DCM_SUBMIT_H_MAIN submit_h_SOURCES = submit-h.c submit-h.h log.c log.h tm.c tm.h -submit_h_LDADD = $(CURL_LIBS) $(XML_LIBS) $(TALLOC_LIBS) $(LTLIBICONV) \ - $(POPT_LIBS) +submit_h_LDADD = $(NSS_LIBS) $(OPENSSL_LIBS) $(CURL_LIBS) $(XML_LIBS) $(TALLOC_LIBS) \ + $(LTLIBICONV) $(POPT_LIBS) .PHONY: manlint manlint: $(man_MANS) diff --git a/src/certsave-o.c b/src/certsave-o.c index b145a21..caab151 100644 --- a/src/certsave-o.c +++ b/src/certsave-o.c @@ -285,7 +285,7 @@ cm_certsave_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, /* Double-check that we're not trying to rotate in a * key that we won't actually be using. */ if ((old_pkey != NULL) && - (EVP_PKEY_cmp(old_pkey, X509_get_pubkey(cert)) == 1)) { + (EVP_PKEY_eq(old_pkey, X509_get_pubkey(cert)) == 1)) { entry->cm_key_next_marker = NULL; if (next_key != NULL) { cm_log(1, "Public key was not changed.\n"); diff --git a/src/keygen-o.c b/src/keygen-o.c index 676db8b..ff929fd 100644 --- a/src/keygen-o.c +++ b/src/keygen-o.c @@ -99,6 +99,8 @@ cm_keygen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, struct cm_pin_cb_data cb_data; FILE *fp, *status; EVP_PKEY *pkey; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *check_ctx = NULL; char buf[LINE_MAX], *pin, *pubhex, *pubihex, *oldfile; unsigned char *p, *q; long error, errno_save; @@ -106,13 +108,9 @@ cm_keygen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, int cm_key_size; int len; int keyfd; + int ret; char *filename; char *marker; - BIGNUM *exponent; - RSA *rsa; -#ifdef CM_ENABLE_DSA - DSA *dsa; -#endif #ifdef CM_ENABLE_EC EC_KEY *ec; int ecurve; @@ -146,95 +144,155 @@ retry_gen: } switch (cm_key_algorithm) { case cm_key_rsa: - exponent = BN_new(); - if (exponent == NULL) { - cm_log(1, "Error setting up exponent.\n"); + unsigned int exponent = CM_DEFAULT_RSA_EXPONENT; + unsigned int bits = cm_key_size; + OSSL_PARAM params[3]; + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (pctx == NULL) { + cm_log(1, "Error allocating new RSA context.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - BN_set_word(exponent, CM_DEFAULT_RSA_EXPONENT); - rsa = RSA_new(); - if (rsa == NULL) { - cm_log(1, "Error allocating new RSA key.\n"); + + if (EVP_PKEY_keygen_init(pctx) == 0) { + cm_log(1, "Error initializing RSA key generation.\n"); + EVP_PKEY_CTX_free(pctx); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (RSA_generate_key_ex(rsa, cm_key_size, exponent, NULL) != 1) { + + params[0] = OSSL_PARAM_construct_uint("bits", &bits); + params[1] = OSSL_PARAM_construct_uint("e", &exponent); + params[2] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, params) == 0) { + cm_log(1, "Error setting RSA key parameters.\n"); + EVP_PKEY_CTX_free(pctx); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + if (EVP_PKEY_generate(pctx, &pkey) != 1) { if (cm_key_size != CM_DEFAULT_PUBKEY_SIZE) { - cm_log(1, "Error generating %d-bit key, " + cm_log(1, "Error generating RSA %d-bit key, " "attempting %d bits.\n", cm_key_size, CM_DEFAULT_PUBKEY_SIZE); cm_key_size = CM_DEFAULT_PUBKEY_SIZE; + EVP_PKEY_CTX_free(pctx); goto retry_gen; } - cm_log(1, "Error generating key.\n"); + cm_log(1, "Error generating RSA key.\n"); + EVP_PKEY_CTX_free(pctx); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (RSA_check_key(rsa) != 1) { /* should be unnecessary */ - cm_log(1, "Key fails checks. Retrying.\n"); - goto retry_gen; + EVP_PKEY_CTX_free(pctx); + + check_ctx = EVP_PKEY_CTX_new(pkey, NULL); + + ret = EVP_PKEY_check(check_ctx); + if (ret == 0) { + cm_log(1, "keygen-o: EVP_PKEY_check): Key pair is invalid.\n"); + EVP_PKEY_CTX_free(check_ctx); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } else if (ret == -2) { + cm_log(1, "keygen-o: EVP_PKEY_check: RSA operation not supported for this algorithm.\n"); + EVP_PKEY_CTX_free(check_ctx); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - EVP_PKEY_set1_RSA(pkey, rsa); + EVP_PKEY_CTX_free(check_ctx); break; #ifdef CM_ENABLE_DSA case cm_key_dsa: - dsa = DSA_new(); - if (dsa == NULL) { - cm_log(1, "Error allocating new DSA key.\n"); + EVP_PKEY_CTX *pctx_params = NULL; + EVP_PKEY* pkey_params = NULL; + + bits = cm_key_size; + + pctx_params = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL); + if (pctx_params == NULL) { + cm_log(1, "keygen-o: Error allocating new DSA params context.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (DSA_generate_parameters_ex(dsa, cm_key_size, - NULL, 0, - NULL, NULL, NULL) != 1) { + + if (EVP_PKEY_paramgen_init(pctx_params) == 0) { + cm_log(1, "keygen-o: Error initializing DSA param generation.\n"); + EVP_PKEY_CTX_free(pctx_params); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + if (EVP_PKEY_CTX_set_dsa_paramgen_bits(pctx_params, bits) == 0) { + cm_log(1, "keygen-o: Error setting DSA bits to %d.\n", bits); + EVP_PKEY_CTX_free(pctx_params); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + if (EVP_PKEY_paramgen(pctx_params, &pkey_params) != 1) { + cm_log(1, "keygen-o: Error in DSA paramgen.\n"); + EVP_PKEY_CTX_free(pctx_params); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + pctx = EVP_PKEY_CTX_new(pkey_params, NULL); + if (EVP_PKEY_keygen_init(pctx) == 0) { + cm_log(1, "keygen-o: Error initializing parameter DSA key generation.\n"); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(pctx_params); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + if (EVP_PKEY_generate(pctx, &pkey) != 1) { if (cm_key_size != CM_DEFAULT_PUBKEY_SIZE) { - cm_log(1, "Error generating %d-bit key, " + cm_log(1, "keygen-o: Error generating DSA %d-bit key, " "attempting %d bits.\n", cm_key_size, CM_DEFAULT_PUBKEY_SIZE); cm_key_size = CM_DEFAULT_PUBKEY_SIZE; + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(pctx_params); goto retry_gen; } - cm_log(1, "Error generating parameters.\n"); + cm_log(1, "Error generating DSA key.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (DSA_generate_key(dsa) != 1) { - cm_log(1, "Error generating key.\n"); + EVP_PKEY_free(pkey_params); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(pctx_params); + + check_ctx = EVP_PKEY_CTX_new(pkey, NULL); + ret = EVP_PKEY_param_check(check_ctx); + if (ret == 0) { + cm_log(1, "keygen-o: DSA key pair is invalid\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } else if (ret == -2) { + cm_log(1, "keygen-o: EVP_PKEY_check: DSA operation not supported for this algorithm.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - EVP_PKEY_set1_DSA(pkey, dsa); + EVP_PKEY_CTX_free(check_ctx); break; #endif #ifdef CM_ENABLE_EC case cm_key_ecdsa: + char *curve = NULL; if (cm_key_size <= 256) - ecurve = NID_X9_62_prime256v1; + // ecurve = NID_X9_62_prime256v1; + curve = "prime256v1"; else if (cm_key_size <= 384) - ecurve = NID_secp384r1; + // ecurve = NID_secp384r1; + curve = "secp384r1"; else - ecurve = NID_secp521r1; - ec = EC_KEY_new_by_curve_name(ecurve); - while ((ec == NULL) && (ecurve != NID_X9_62_prime256v1)) { - cm_log(1, "Error allocating new EC key.\n"); - switch (ecurve) { - case NID_secp521r1: - cm_log(1, "Trying with a smaller key.\n"); - ecurve = NID_secp384r1; - ec = EC_KEY_new_by_curve_name(ecurve); - break; - case NID_secp384r1: - cm_log(1, "Trying with a smaller key.\n"); - ecurve = NID_X9_62_prime256v1; - ec = EC_KEY_new_by_curve_name(ecurve); - break; + // ecurve = NID_secp521r1; + curve = "secp521r1"; + + pkey = EVP_EC_gen(curve); + if (pkey == NULL) { + if (strcmp(curve, "prime256v1") == 0) { + cm_log(1, "Error generating EC %s key, " + "attempting prime256v1.\n", + curve); + curve = "prime256v1"; + EVP_PKEY_CTX_free(pctx); + goto retry_gen; } - } - if (ec == NULL) { - cm_log(1, "Error allocating new EC key.\n"); - _exit(CM_SUB_STATUS_INTERNAL_ERROR); - } - if (EC_KEY_generate_key(ec) != 1) { - cm_log(1, "Error generating key.\n"); + cm_log(1, "Error generating RSA key.\n"); + EVP_PKEY_CTX_free(pctx); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); - EVP_PKEY_set1_EC_KEY(pkey, ec); + EVP_PKEY_CTX_free(pctx); break; #endif default: @@ -372,6 +430,7 @@ retry_gen: fprintf(status, "%s\n%s\n%s\n", pubihex, pubhex, marker); fclose(fp); fclose(status); + EVP_PKEY_free(pkey); /* Try to remove any keys with old candidate names. */ if ((entry->cm_key_next_marker != NULL) && diff --git a/src/local.c b/src/local.c index a62ebda..7d2e942 100644 --- a/src/local.c +++ b/src/local.c @@ -199,11 +199,14 @@ get_signer_info(void *parent, char *localdir, X509 ***roots, char *creds, *hexserial = NULL, *serial, buf[LINE_MAX], *csr; STACK_OF(X509) *cas = NULL; PKCS12 *p12 = NULL; - BIGNUM *exponent = NULL; - RSA *rsa; + unsigned int bits = CM_DEFAULT_PUBKEY_SIZE; + unsigned int exponent = CM_DEFAULT_RSA_EXPONENT; + OSSL_PARAM rsa_params[3]; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *check_ctx = NULL; dbus_bool_t save = FALSE; time_t now, then, life, lifedelta; - int i; + int i, ret; *roots = NULL; *signer_cert = NULL; @@ -301,29 +304,39 @@ get_signer_info(void *parent, char *localdir, X509 ***roots, } /* Generate a new key. For now at least, generate RSA of the * default size with the default exponent. */ - exponent = BN_new(); - if (exponent == NULL) { - cm_log(1, "Error setting up exponent.\n"); + pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (pctx == NULL) { + cm_log(1, "Error allocating new RSA context.\n"); return CM_SUBMIT_STATUS_UNREACHABLE; } - BN_set_word(exponent, CM_DEFAULT_RSA_EXPONENT); - rsa = RSA_new(); - if (rsa == NULL) { - cm_log(1, "Error allocating new RSA key.\n"); + + if (EVP_PKEY_keygen_init(pctx) == 0) { + cm_log(1, "Error initializing RSA key generation.\n"); + return CM_SUBMIT_STATUS_UNREACHABLE; + } + + rsa_params[0] = OSSL_PARAM_construct_uint("bits", &bits); + rsa_params[1] = OSSL_PARAM_construct_uint("e", &exponent); + rsa_params[2] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, rsa_params) == 0) { + cm_log(1, "Error setting RSA key parameters.\n"); return CM_SUBMIT_STATUS_UNREACHABLE; } + + retry_gen: - if (RSA_generate_key_ex(rsa, CM_DEFAULT_PUBKEY_SIZE, exponent, - NULL) != 1) { - cm_log(1, "Error generating key.\n"); + if (EVP_PKEY_generate(pctx, signer_key) != 1) { + cm_log(1, "Error generating RSA %d-bit key.\n", bits); + EVP_PKEY_CTX_free(pctx); return CM_SUBMIT_STATUS_UNREACHABLE; } - if (RSA_check_key(rsa) != 1) { /* should be unnecessary */ + check_ctx = EVP_PKEY_CTX_new(*signer_key, NULL); + + ret = EVP_PKEY_check(check_ctx); + if (ret == 0) { cm_log(1, "Key fails checks. Retrying.\n"); goto retry_gen; } - *signer_key = EVP_PKEY_new(); - EVP_PKEY_set1_RSA(*signer_key, rsa); /* Build a suitable CA signing request. */ csr = make_ca_csr(parent, *signer_key, *signer_cert); if (csr == NULL) { diff --git a/src/log.c b/src/log.c index 4cc8368..b0a1a31 100644 --- a/src/log.c +++ b/src/log.c @@ -28,6 +28,9 @@ #include #include +#include +#include +#include #include #include "log.h" @@ -112,3 +115,28 @@ cm_log(int level, const char *fmt, ...) } } } + +/* Log the passed message at level and then display all errors reported + * by OpenSSL and NSS. Then clear all errors in both. + */ +void +cm_log_errors(int level, char *msg) +{ + char buf[LINE_MAX] = ""; + long error; + int nss_err; + + cm_log(level, "%s\n", msg); + while ((error = ERR_get_error()) != 0) { + memset(buf, '\0', sizeof(buf)); + ERR_error_string_n(error, buf, sizeof(buf)); + cm_log(level, "%s\n", buf); + } + ERR_clear_error(); + + nss_err = PORT_GetError(); + if (nss_err < 0) { + cm_log(level, "%d: %s\n", nss_err, PR_ErrorToString(nss_err, 0)); + } + PORT_SetError(0); +} diff --git a/src/log.h b/src/log.h index e4645e1..5ad2fd5 100644 --- a/src/log.h +++ b/src/log.h @@ -43,5 +43,6 @@ void cm_log(int level, const char *fmt, ...) __attribute__((format(printf,2,3))) #endif ; +void cm_log_errors(int level, char *msg); #endif diff --git a/src/pkcs7.c b/src/pkcs7.c index b2cccc0..3b94043 100644 --- a/src/pkcs7.c +++ b/src/pkcs7.c @@ -275,25 +275,6 @@ cm_pkcs7_parse_buffer(const unsigned char *buffer, size_t length, } } -void -log_pkcs7_errors(int level, char *msg) -{ - char buf[LINE_MAX] = ""; - long error; - int nss_err; - - cm_log(level, "%s\n", msg); - while ((error = ERR_get_error()) != 0) { - memset(buf, '\0', sizeof(buf)); - ERR_error_string_n(error, buf, sizeof(buf)); - cm_log(level, "%s\n", buf); - } - nss_err = PORT_GetError(); - if (nss_err < 0) { - cm_log(level, "%d: %s\n", nss_err, PR_ErrorToString(nss_err, 0)); - } -} - int cm_pkcs7_parsev(unsigned int flags, void *parent, char **certleaf, char **certtop, char ***certothers, @@ -545,7 +526,7 @@ cm_pkcs7_envelope_data(char *encryption_cert, enum cm_prefs_cipher cipher, } recipient = PEM_read_bio_X509(in, NULL, NULL, NULL); if (recipient == NULL) { - log_pkcs7_errors(0, "Error parsing recipient certificate.\n"); + cm_log_errors(0, "Error parsing recipient certificate.\n"); goto done; } BIO_free(in); @@ -567,12 +548,12 @@ cm_pkcs7_envelope_data(char *encryption_cert, enum cm_prefs_cipher cipher, BIO_free(in); if (p7 == NULL) { - log_pkcs7_errors(0, "Error encrypting signing request.\n"); + cm_log_errors(0, "Error encrypting signing request.\n"); goto done; } len = i2d_PKCS7(p7, NULL); if (len < 0) { - log_pkcs7_errors(0, "Error encoding encrypted signing request.\n"); + cm_log_errors(0, "Error encoding encrypted signing request.\n"); goto done; } dp7 = malloc(len); @@ -582,7 +563,7 @@ cm_pkcs7_envelope_data(char *encryption_cert, enum cm_prefs_cipher cipher, } u = dp7; if (i2d_PKCS7(p7, &u) != len) { - log_pkcs7_errors(0, "Error encoding encrypted signing request.\n"); + cm_log_errors(0, "Error encoding encrypted signing request.\n"); goto done; } *enveloped = dp7; @@ -619,13 +600,13 @@ cm_pkcs7_envelope_csr(char *encryption_cert, enum cm_prefs_cipher cipher, req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL); BIO_free(in); if (req == NULL) { - log_pkcs7_errors(0, "Error parsing certificate signing request.\n"); + cm_log_errors(0, "Error parsing certificate signing request.\n"); goto done; } dlen = i2d_X509_REQ(req, NULL); if (dlen < 0) { - log_pkcs7_errors(0, "Error encoding certificate signing request.\n"); + cm_log_errors(0, "Error encoding certificate signing request.\n"); goto done; } dreq = malloc(dlen); @@ -635,7 +616,7 @@ cm_pkcs7_envelope_csr(char *encryption_cert, enum cm_prefs_cipher cipher, } u = dreq; if (i2d_X509_REQ(req, &u) != dlen) { - log_pkcs7_errors(0, "Error encoding certificate signing request.\n"); + cm_log_errors(0, "Error encoding certificate signing request.\n"); goto done; } ret = cm_pkcs7_envelope_data(encryption_cert, cipher, dreq, dlen, @@ -697,7 +678,7 @@ cm_pkcs7_generate_ias(char *cacert, char *minicert, ca = PEM_read_bio_X509(in, NULL, NULL, NULL); BIO_free(in); if (ca == NULL) { - log_pkcs7_errors(0, "Error parsing CA certificate.\n"); + cm_log_errors(0, "Error parsing CA certificate.\n"); goto done; } @@ -709,7 +690,7 @@ cm_pkcs7_generate_ias(char *cacert, char *minicert, mini = PEM_read_bio_X509(in, NULL, NULL, NULL); BIO_free(in); if (mini == NULL) { - log_pkcs7_errors(0, "Error parsing client certificate.\n"); + cm_log_errors(0, "Error parsing client certificate.\n"); goto done; } @@ -725,7 +706,7 @@ cm_pkcs7_generate_ias(char *cacert, char *minicert, } u = issuer; if (i2d_X509_NAME(X509_get_issuer_name(ca), &u) != issuerlen) { - log_pkcs7_errors(0, "Error encoding CA certificate issuer name.\n"); + cm_log_errors(0, "Error encoding CA certificate issuer name.\n"); goto done; } @@ -741,7 +722,7 @@ cm_pkcs7_generate_ias(char *cacert, char *minicert, } u = subject; if (i2d_X509_NAME(X509_get_subject_name(mini), &u) != subjectlen) { - log_pkcs7_errors(0, "Error encoding client certificate subject name.\n"); + cm_log_errors(0, "Error encoding client certificate subject name.\n"); goto done; } PORT_SetError(0); @@ -753,7 +734,7 @@ cm_pkcs7_generate_ias(char *cacert, char *minicert, issuerandsubject.subject.len = subjectlen; if (SEC_ASN1EncodeItem(NULL, &encoded, &issuerandsubject, cm_pkcs7_ias_template) != &encoded) { - log_pkcs7_errors(0, "Error encoding issuer and subject names.\n"); + cm_log_errors(0, "Error encoding issuer and subject names.\n"); goto done; } *ias = malloc(encoded.len); diff --git a/src/scep.c b/src/scep.c index 120bd3a..c9dd10e 100644 --- a/src/scep.c +++ b/src/scep.c @@ -441,7 +441,7 @@ main(int argc, const char **argv) NULL, NULL, NULL, NULL, NULL, NULL); if (i != 0) { - log_pkcs7_errors(0, "Error: failed to verify signature on " + cm_log_errors(0, "Error: failed to verify signature on " "rekey PKCSReq.\n"); } if ((msgtype == NULL) || @@ -471,7 +471,7 @@ main(int argc, const char **argv) &sent_nonce, &sent_nonce_length, NULL, NULL, NULL, NULL); if (i != 0) { - log_pkcs7_errors(0, "Error: failed to verify signature on " + cm_log_errors(0, "Error: failed to verify signature on " "message.\n"); } if ((msgtype == NULL) || @@ -1005,7 +1005,7 @@ main(int argc, const char **argv) printf("%s", buf); } printf("\n"); - log_pkcs7_errors(0, "Error: failed to verify signature on " + cm_log_errors(0, "Error: failed to verify signature on " "server response.\n"); s = cm_store_base64_from_bin(ctx, (unsigned char *) results2, results_length2); @@ -1126,7 +1126,7 @@ main(int argc, const char **argv) p7 = d2i_PKCS7(NULL, &u, payload_length); if (p7 == NULL) { printf(_("Error: couldn't parse signed-data.\n")); - log_pkcs7_errors(0, "Error: couldn't parse signed-data.\n"); + cm_log_errors(0, "Error: couldn't parse signed-data.\n"); s = cm_store_base64_from_bin(ctx, (unsigned char *) results2, results_length2); @@ -1138,7 +1138,7 @@ main(int argc, const char **argv) } if (!PKCS7_type_is_enveloped(p7)) { printf(_("Error: signed-data payload is not enveloped-data.\n")); - log_pkcs7_errors(0, "Error: signed-data payload is not " + cm_log_errors(0, "Error: signed-data payload is not " "enveloped-data.\n"); s = cm_store_base64_from_bin(ctx, (unsigned char *) results2, @@ -1154,7 +1154,7 @@ main(int argc, const char **argv) (p7->d.enveloped->enc_data->content_type == NULL) || (OBJ_obj2nid(p7->d.enveloped->enc_data->content_type) != NID_pkcs7_data)) { printf(_("Error: enveloped-data payload is not data.\n")); - log_pkcs7_errors(0, "Error: enveloped-data payload is " + cm_log_errors(0, "Error: enveloped-data payload is " "not data.\n"); s = cm_store_base64_from_bin(ctx, (unsigned char *) results2, diff --git a/src/scepgen-n.c b/src/scepgen-n.c index e5a0a81..46244de 100644 --- a/src/scepgen-n.c +++ b/src/scepgen-n.c @@ -134,10 +134,14 @@ cm_scepgen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, struct cm_keyiread_n_ctx_and_keys *keys; const char *p, *es, *reason; int ec; + int ret; PKCS7 *csr_new, *csr_old, *ias_new, *ias_old; + unsigned int bits = CM_DEFAULT_PUBKEY_SIZE; + unsigned int exponent = CM_DEFAULT_RSA_EXPONENT; EVP_PKEY *key; - RSA *rsa; - BIGNUM *exponent; + OSSL_PARAM params[3]; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *check_ctx = NULL; status = fdopen(fd, "w"); if (status == NULL) { @@ -205,32 +209,48 @@ cm_scepgen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, /* Use a dummy key to sign using OpenSSL. */ cm_log(1, "Generating dummy key.\n"); + pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (pctx == NULL) { + cm_log(1, "Error allocating new RSA context.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + if (EVP_PKEY_keygen_init(pctx) == 0) { + cm_log(1, "Error initializing RSA key generation.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + key = EVP_PKEY_new(); if (key == NULL) { cm_log(0, "Error allocating new key.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - exponent = BN_new(); - if (exponent == NULL) { - cm_log(0, "Error setting up exponent.\n"); - _exit(CM_SUB_STATUS_INTERNAL_ERROR); - } - BN_set_word(exponent, CM_DEFAULT_RSA_EXPONENT); - rsa = RSA_new(); - if (rsa == NULL) { - cm_log(0, "Error allocating new RSA key.\n"); + params[0] = OSSL_PARAM_construct_uint("bits", &bits); + params[1] = OSSL_PARAM_construct_uint("e", &exponent); + params[2] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, params) == 0) { + cm_log(1, "Error setting RSA key parameters.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } + retry_gen: - if (RSA_generate_key_ex(rsa, CM_DEFAULT_PUBKEY_SIZE, exponent, NULL) != 1) { - cm_log(0, "Error generating key.\n"); + if (EVP_PKEY_generate(pctx, &key) != 1) { + cm_log(1, "Error generating RSA %d-bit key.\n", bits); + EVP_PKEY_CTX_free(pctx); _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (RSA_check_key(rsa) != 1) { /* should be unnecessary */ - cm_log(1, "Key fails checks. Retrying.\n"); + + check_ctx = EVP_PKEY_CTX_new(key, NULL); + + ret = EVP_PKEY_check(check_ctx); + if (ret == 0) { + cm_log(1, "EVP_PKEY_check (pairwise check): Key pair is invalid.\n"); goto retry_gen; + } else if (ret == -2) { + cm_log(1, "EVP_PKEY_check: Operation not supported for this algorithm.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - BN_free(exponent); + EVP_PKEY_CTX_free(check_ctx); /* Read the proper keys. */ keys = cm_keyiread_n_get_keys(entry, 0); @@ -242,7 +262,6 @@ retry_gen: } /* Sign using a dummy key. */ - EVP_PKEY_set1_RSA(key, rsa); csr_new = NULL; csr_old = NULL; ias_new = NULL; diff --git a/src/submit-n.c b/src/submit-n.c index 2b64902..dcb88b8 100644 --- a/src/submit-n.c +++ b/src/submit-n.c @@ -93,10 +93,14 @@ try_to_decode(void *parent, PLArenaPool *arena, SECItem *item, ASN1_STRING *params = NULL; PKCS7 *p7 = NULL; PKCS7_RECIP_INFO *p7i = NULL; - BIGNUM *exponent = NULL; EVP_PKEY *pkey = NULL; + unsigned int bits = CM_DEFAULT_PUBKEY_SIZE; + unsigned int exponent = CM_DEFAULT_RSA_EXPONENT; + OSSL_PARAM rsa_params[3]; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *check_ctx = NULL; + int rc = 0; BIO *out; - RSA *rsa = NULL; char buf[BUFSIZ]; const unsigned char *u; unsigned char *enc_key, *dec, *reenc, *param_data; @@ -191,36 +195,69 @@ try_to_decode(void *parent, PLArenaPool *arena, SECItem *item, pkey = EVP_PKEY_new(); if (pkey == NULL) { cm_log(1, "Error allocating new key.\n"); - goto done; + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - exponent = BN_new(); - if (exponent == NULL) { - cm_log(1, "Error setting up exponent.\n"); - goto done; +retry_gen: + pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (pctx == NULL) { + cm_log(1, "Error allocating new RSA context.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - BN_set_word(exponent, CM_DEFAULT_RSA_EXPONENT); - rsa = RSA_new(); - if (rsa == NULL) { - cm_log(1, "Error allocating new RSA key.\n"); - goto done; + + if (EVP_PKEY_keygen_init(pctx) == 0) { + cm_log(1, "Error initializing RSA key generation.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } -retry_gen: - if (RSA_generate_key_ex(rsa, CM_DEFAULT_PUBKEY_SIZE, exponent, NULL) != 1) { - cm_log(1, "Error generating key.\n"); - goto done; + + + rsa_params[0] = OSSL_PARAM_construct_uint("bits", &bits); + rsa_params[1] = OSSL_PARAM_construct_uint("e", &exponent); + rsa_params[2] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, rsa_params) == 0) { + cm_log(1, "Error setting RSA key parameters.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + if (EVP_PKEY_generate(pctx, &pkey) != 1) { + cm_log(1, "Error generating RSA %d-bit key.\n", bits); + EVP_PKEY_CTX_free(pctx); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - if (RSA_check_key(rsa) != 1) { /* should be unnecessary */ - cm_log(1, "Key fails checks. Retrying.\n"); + + check_ctx = EVP_PKEY_CTX_new(pkey, NULL); + + rc = EVP_PKEY_check(check_ctx); + if (rc == 0) { + cm_log(1, "submit-n: EVP_PKEY_check (pairwise check): Key pair %d bits is invalid.\n", bits); + + EVP_PKEY_CTX_free(pctx); goto retry_gen; + } else if (rc == -2) { + cm_log(1, "EVP_PKEY_check: Operation not supported for this algorithm.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); } - EVP_PKEY_set1_RSA(pkey, rsa); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(check_ctx); /* Encrypt the bulk key. We're about to decrypt it again, so do it the * simplest way that we can. */ - reenc_len = dec_len + RSA_size(rsa); + pctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL); + if (pctx == NULL) { + cm_log(1, "submit-n: Error allocating new RSA context.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + if (EVP_PKEY_encrypt_init(pctx) <= 0) { + cm_log(1, "submit-n: Error initializing RSA encrypt context.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0) { + cm_log(1, "submit-n: Error setting RSA encrypt padding to RSA_PKCS1_PADDING\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + + reenc_len = dec_len + EVP_PKEY_size(pkey); reenc = talloc_size(parent, reenc_len); - padding = RSA_PKCS1_PADDING; - reenc_len = RSA_public_encrypt(dec_len, dec, reenc, rsa, padding); + EVP_PKEY_encrypt(pctx, reenc, &reenc_len, dec, dec_len); if (reenc_len < 0) { cm_log(1, "Error reencrypting.\n"); goto retry_gen; @@ -253,17 +290,18 @@ retry_gen: done: if (ret == NULL) { + cm_log(1, "something failed, ret is NULL\n"); while ((error = ERR_get_error()) != 0) { ERR_error_string_n(error, buf, sizeof(buf)); cm_log(1, "%s\n", buf); } } + if (pctx) { + EVP_PKEY_CTX_free(pctx); + } if (pkey != NULL) { EVP_PKEY_free(pkey); } - if (exponent != NULL) { - BN_free(exponent); - } if (p7 != NULL) { PKCS7_free(p7); } From 4718c35004d52ad143885eec299dd6fe7400c424 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 2/12] Add initial ML-DSA support with OpenSSL 3.5.0 OpenSSL 3.5.0 has added support for Post-Quantum ML-DSA keys. Allow them to be generated by certmonger. The minimum key size is not fully implemented because key size has a different meaning in this context. It is plumbed in, such that one can set a numeric size, but it isn't used and is effectively meaningless. --- diff --git a/configure.ac b/configure.ac index 7ec488c..ecc35e1 100644 --- a/configure.ac +++ b/configure.ac @@ -714,6 +714,54 @@ if ! ${configure_dist_target_only:-false} ; then AC_SUBST(MAN_EC) AC_SUBST(NO_MAN_EC) + AC_ARG_ENABLE(ml_dsa, + AS_HELP_STRING(--disable-ml-dsa,[Disable ML-DSA key support, even if available.]), + ml_dsa=$enableval, + ml_dsa=maybe) + if test x$ml_dsa != xno ; then + CFLAGSsave="$CFLAGS" + LIBSsave="$LIBS" + CFLAGS="$OPENSSL_CFLAGS $CFLAGS" + LIBS="$OPENSSL_LIBS $LIBS" + AC_CHECK_LIB(crypto,EVP_PKEY_Q_keygen) + # CFLAGS="$NSS_CFLAGS $CFLAGS" + # LIBS="$NSS_LIBS $LIBS" + # AC_CHECK_FUNCS(SECKEY_CreateECPrivateKey) + # AC_CHECK_FUNCS(PK11_PrivDecrypt) + # AC_CHECK_FUNCS(NSS_OptionGet NSS_OptionSet) + CFLAGS="$CFLAGSsave" + LIBS="$LIBSsave" + can_ml_dsa=true + if test x$ac_cv_lib_crypto_EVP_PKEY_Q_keygen = xno ; then + can_ml_dsa=false + fi + AC_MSG_CHECKING([for ML-DSA support]) + if $can_ml_dsa ; then + AC_MSG_RESULT(enabling ML-DSA support) + AC_DEFINE_UNQUOTED(CM_ENABLE_ML_DSA,1,[Define to enable ML-DSA support.]) + MAN_ML_DSA="" + NO_MAN_ML_DSA=".\\\" " + ml_dsa=yes + else + if test x$ml_dsa != xyes ; then + AC_MSG_WARN([unavailable, disabling]) + ml_dsa=no + else + AC_MSG_ERROR([unavailable]) + fi + MAN_ML_DSA=".\\\" " + NO_MAN_EC="" + fi + else + AC_MSG_RESULT(NOT enabling ML-DSA support) + MAN_ML_DSA=".\\\" " + NO_MAN_ML_DSA="" + ml_dsa=no + fi + AM_CONDITIONAL(HAVE_ML_DSA,[test x$ml_dsa = xyes]) + AC_SUBST(MAN_ML_DSA) + AC_SUBST(NO_MAN_ML_DSA) + AC_DEFINE_UNQUOTED(CM_DEFAULT_KEY_STORAGE_TYPE,cm_key_storage_nssdb,[Define to the default type of storage used for keys.]) AC_DEFINE_UNQUOTED(CM_DEFAULT_KEY_STORAGE_LOCATION,"/etc/pki/nssdb",[Define to the default location of storage used for keys.]) AC_DEFINE_UNQUOTED(CM_DEFAULT_KEY_TOKEN,NULL,[Define to the default token used for holding keys.]) @@ -732,9 +780,11 @@ if ! ${configure_dist_target_only:-false} ; then AC_SUBST(CM_MINIMUM_RSA_KEY_SIZE) CM_MINIMUM_DSA_KEY_SIZE=512 CM_MINIMUM_EC_KEY_SIZE=256 + CM_MINIMUM_ML_DSA_KEY_SIZE=44 AC_DEFINE_UNQUOTED(CM_MINIMUM_RSA_KEY_SIZE,$CM_MINIMUM_RSA_KEY_SIZE,[Define to the minimum key size when generating RSA keys. Requests to generate smaller keys will be forced to this key size.]) AC_DEFINE_UNQUOTED(CM_MINIMUM_DSA_KEY_SIZE,$CM_MINIMUM_DSA_KEY_SIZE,[Define to the minimum key size when generating DSA parameters and keys. Requests to generate smaller keys will be forced to this key size.]) AC_DEFINE_UNQUOTED(CM_MINIMUM_EC_KEY_SIZE,$CM_MINIMUM_EC_KEY_SIZE,[Define to the minimum key size when selecting elliptic curve parameters. Requests to generate smaller keys will be forced to this key size.]) + AC_DEFINE_UNQUOTED(CM_MINIMUM_ML_DSA_KEY_SIZE,$CM_MINIMUM_ML_DSA_KEY_SIZE,[Define to the minimum key size when selecting ML-DSA parameters. Requests to generate smaller keys will be forced to this key size.]) AC_SUBST(CM_MINIMUM_RSA_KEY_SIZE) AC_SUBST(CM_MINIMUM_DSA_KEY_SIZE) AC_SUBST(CM_MINIMUM_EC_KEY_SIZE) diff --git a/src/certmonger.conf.5.in b/src/certmonger.conf.5.in index 49b6e09..de41105 100644 --- a/src/certmonger.conf.5.in +++ b/src/certmonger.conf.5.in @@ -49,6 +49,7 @@ signing requests, and used when self\-signing certificates. @NO_MAN_DSA@\fIRSA\fR is supported. @MAN_DSA@\fIRSA\fR and \fIDSA\fR are supported. @MAN_EC@\fIEC\fR (also known as \fIECDSA\fR) is also supported. +@MAN_ML_DSA@\fIML-DSA-44, ML-DSA-65, ML-DSA-87\fR are also supported. The default is \fIRSA\fP. .IP rsa_key_size diff --git a/src/getcert.c b/src/getcert.c index b4a02d4..a835328 100644 --- a/src/getcert.c +++ b/src/getcert.c @@ -895,6 +895,11 @@ request(const char *argv0, int argc, const char **argv) && (strcasecmp(poptarg, "ECDSA") != 0) && (strcasecmp(poptarg, "EC") != 0) #endif +#ifdef CM_ENABLE_ML_DSA + && (strcasecmp(poptarg, "ML-DSA-44") != 0) + && (strcasecmp(poptarg, "ML-DSA-65") != 0) + && (strcasecmp(poptarg, "ML-DSA-87") != 0) +#endif ) { printf(_("No support for generating \"%s\" keys.\n"), poptarg); @@ -906,6 +911,9 @@ request(const char *argv0, int argc, const char **argv) #ifdef CM_ENABLE_EC printf(" EC"); #endif +#ifdef CM_ENABLE_ML_DSA + printf(" ML-DSA-44 ML-DA-65 ML-DSA-87"); +#endif printf("\n"); return 1; } @@ -2702,6 +2710,9 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, && (strcasecmp(poptarg, "ECDSA") != 0) && (strcasecmp(poptarg, "EC") != 0) #endif +#ifdef CM_ENABLE_ML_DSA + && (strcasecmp(poptarg, "ML-DSA-65") != 0) +#endif ) { printf(_("No support for generating \"%s\" keys.\n"), poptarg); diff --git a/src/keygen-n.c b/src/keygen-n.c index 27c1efc..3c3b6cd 100644 --- a/src/keygen-n.c +++ b/src/keygen-n.c @@ -271,6 +271,14 @@ cm_keygen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pmech = CKM_EC_KEY_PAIR_GEN; break; #endif +#ifdef CM_ENABLE_ML_DSA: + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + mech = CKM_ML_DSA_KEY_PAIR_GEN; + pmech = CKM_ML_DSA_KEY_PAIR_GEN; + break; +#endif default: fprintf(status, "Unknown or unsupported key type.\n"); cm_log(1, "Unknown or unsupported key type.\n"); @@ -530,6 +538,13 @@ retry_gen: /* no parameters to generate */ break; #endif +#ifdef CM_ENABLE_ML_DSA: + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + /* no parameters to generate */ + break; +#endif default: params = NULL; break; @@ -565,6 +580,13 @@ retry_gen: params = &ec_params; break; #endif +#ifdef CM_ENABLE_ML_DSA: + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + /* FIXME */ + break; +#endif default: params = NULL; break; diff --git a/src/keygen-o.c b/src/keygen-o.c index ff929fd..2f5a212 100644 --- a/src/keygen-o.c +++ b/src/keygen-o.c @@ -111,10 +111,6 @@ cm_keygen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, int ret; char *filename; char *marker; -#ifdef CM_ENABLE_EC - EC_KEY *ec; - int ecurve; -#endif status = fdopen(fd, "w"); if (status == NULL) { @@ -295,12 +291,38 @@ retry_gen: EVP_PKEY_CTX_free(pctx); break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + const char *alg = cm_store_algorithm_to_name(cm_key_algorithm); + EVP_PKEY_CTX *ctx = NULL; + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, alg, NULL)) == NULL) { + ERR_print_errors_fp(stderr); + cm_log(1, "Initializing key context failed\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + if (EVP_PKEY_keygen_init(ctx) <= 0) { + ERR_print_errors_fp(stderr); + cm_log(1, "keygen_init failed\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { + cm_log(1, "Error generating key.\n"); + _exit(CM_SUB_STATUS_INTERNAL_ERROR); + } + EVP_PKEY_CTX_free(ctx); + break; +#endif default: cm_log(1, "Unknown or unsupported key type.\n"); _exit(CM_SUB_STATUS_INTERNAL_ERROR); break; } + char *n = EVP_PKEY_get0_type_name(pkey); + cm_log(1, "debug: generated type %s for %d\n", n, cm_key_algorithm); + filename = strdup(entry->cm_key_storage_location); marker = ""; keyfd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); diff --git a/src/keyiread-n.c b/src/keyiread-n.c index c2f3928..ed87b09 100644 --- a/src/keyiread-n.c +++ b/src/keyiread-n.c @@ -522,6 +522,7 @@ cm_keyiread_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, cm_log(3, "Key is an EC key.\n"); alg = "EC"; break; + /* FIXME: case mlKey? */ case nullKey: default: cm_log(3, "Key is of an unknown type.\n"); @@ -581,6 +582,7 @@ cm_keyiread_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, cm_log(3, "Next key is an EC key.\n"); alg = "EC"; break; + /* FIXME: case mlKey? */ case nullKey: default: cm_log(3, "Next key is of an unknown type.\n"); diff --git a/src/keyiread-o.c b/src/keyiread-o.c index 51f7f82..d014ca7 100644 --- a/src/keyiread-o.c +++ b/src/keyiread-o.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009,2010,2011,2012,2014,2015,2017 Red Hat, Inc. - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -34,6 +34,17 @@ #include #include +/************************ + * FIXME: these definitions aren't public in openssl? wrong algos? +************************/ +#define NID_ML_DSA_44 1457 +#define NID_ML_DSA_65 1458 +#define NID_ML_DSA_87 1459 + +# define EVP_PKEY_ML_DSA_44 NID_ML_DSA_44 +# define EVP_PKEY_ML_DSA_65 NID_ML_DSA_65 +# define EVP_PKEY_ML_DSA_87 NID_ML_DSA_87 + #include #include "keyiread.h" @@ -152,6 +163,8 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pubkey = ""; pubikey = ""; if (pkey != NULL) { + char *n = EVP_PKEY_get0_type_name(pkey); + switch (util_EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: cm_log(3, "Key is an RSA key.\n"); @@ -169,6 +182,24 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, alg = "EC"; break; #endif +#ifdef CM_ENABLE_ML_DSA + case 0: /* no key type base */ + char *name = EVP_PKEY_get0_type_name(pkey); + cm_log(3, "Key is a %s key.\n", name); + + if (strcasecmp(name, "ML-DSA-44") == 0) { + alg = "ML-DSA-44"; + break; + } else + if (strcasecmp(name, "ML-DSA-65") == 0) { + alg = "ML-DSA-65"; + break; + } else + if (strcasecmp(name, "ML-DSA-87") == 0) { + alg = "ML-DSA-87"; + break; + } +#endif default: cm_log(3, "Key is for an unknown algorithm.\n"); alg = ""; @@ -190,6 +221,8 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pubkey = cm_store_hex_from_bin(NULL, tmp, length); } } + } else { + cm_log(1, "pkey IS NULL\n"); } fprintf(fp, "%s/%d/%s/%s\n", alg, bits, pubikey, pubkey); if (nextpkey != NULL) { @@ -210,6 +243,23 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, alg = "EC"; break; #endif +#ifdef CM_ENABLE_ML_DSA + case 0: /* no key type base */ + char *name = EVP_PKEY_get0_type_name(pkey); + + if (strcasecmp(name, "ML-DSA-44") == 0) { + alg = "ML-DSA-44"; + break; + } else + if (strcasecmp(name, "ML-DSA-65") == 0) { + alg = "ML-DSA-65"; + break; + } else + if (strcasecmp(name, "ML-DSA-87") == 0) { + alg = "ML-DSA-87"; + break; + } +#endif default: cm_log(3, "Next key is for an unknown algorithm.\n"); alg = ""; diff --git a/src/keyiread.c b/src/keyiread.c index db34c4b..668b6d3 100644 --- a/src/keyiread.c +++ b/src/keyiread.c @@ -141,8 +141,23 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) (strncasecmp(p, "EC", 2) == 0)) { alg = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (((q - p) == strlen("ML-DSA-44")) && + (strncasecmp(p, "ML-DSA-44", 9) == 0)) { + alg = cm_key_ml_dsa_44; + } else + if (((q - p) == strlen("ML-DSA-65")) && + (strncasecmp(p, "ML-DSA-65", 9) == 0)) { + alg = cm_key_ml_dsa_65; + } else + if (((q - p) == strlen("ML-DSA-87")) && + (strncasecmp(p, "ML-DSA-87", 9) == 0)) { + alg = cm_key_ml_dsa_87; +#endif } else { alg = cm_key_unspecified; + cm_log(1, "debug: determined unspcified from %d\n", alg); } if (alg != cm_key_unspecified) { p = q + strspn(q, "/\r\n"); @@ -150,6 +165,7 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) if (p != q) { size = atoi(p); if (size > 0) { + cm_log(1, "debug: setting alg %d size %d\n", alg, size); entry->cm_key_type.cm_key_algorithm = alg; entry->cm_key_type.cm_key_size = size; if (entry->cm_key_type.cm_key_gen_algorithm == 0) { @@ -202,7 +218,22 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) (strncasecmp(p, "EC", 2) == 0)) { alg = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (((q - p) == strlen("ML-DSA-44")) && + (strncasecmp(p, "ML-DSA-44", 9) == 0)) { + alg = cm_key_ml_dsa_44; + } else + if (((q - p) == strlen("ML-DSA-65")) && + (strncasecmp(p, "ML-DSA-65", 9) == 0)) { + alg = cm_key_ml_dsa_65; + } else + if (((q - p) == strlen("ML-DSA-87")) && + (strncasecmp(p, "ML-DSA-87", 9) == 0)) { + alg = cm_key_ml_dsa_87; +#endif } else { + cm_log(1, "debug: setting next unspcified from %s\n", p); alg = cm_key_unspecified; } if (alg != cm_key_unspecified) { diff --git a/src/prefs.c b/src/prefs.c index 52ffc90..917a9f4 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -516,6 +516,20 @@ cm_prefs_preferred_key_algorithm(void) return cm_key_rsa; } #endif +#ifdef CM_ENABLE_ML_DSA + else if (strcasecmp(keytype, "ML-DSA-44") == 0) { + free(keytype); + return cm_key_ml_dsa_44; + } + else if (strcasecmp(keytype, "ML-DSA-65") == 0) { + free(keytype); + return cm_key_ml_dsa_65; + } + else if (strcasecmp(keytype, "ML-DSA-87") == 0) { + free(keytype); + return cm_key_ml_dsa_87; + } +#endif } return CM_DEFAULT_PUBKEY_TYPE; } diff --git a/src/store-files.c b/src/store-files.c index 848de41..ffa0c93 100644 --- a/src/store-files.c +++ b/src/store-files.c @@ -57,6 +57,7 @@ static unsigned long long cm_entry_name_last, cm_ca_name_last; + enum cm_store_file_field { cm_store_file_field_invalid = 0, cm_store_file_field_id, @@ -841,6 +842,20 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) ret->cm_key_type.cm_key_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(s[i], "ML-DSA-44") == 0) { + ret->cm_key_type.cm_key_algorithm = + cm_key_ml_dsa_44; + } else + if (strcasecmp(s[i], "ML-DSA-65") == 0) { + ret->cm_key_type.cm_key_algorithm = + cm_key_ml_dsa_65; + } else + if (strcasecmp(s[i], "ML-DSA-87") == 0) { + ret->cm_key_type.cm_key_algorithm = + cm_key_ml_dsa_87; +#endif } else { ret->cm_key_type.cm_key_algorithm = cm_key_unspecified; @@ -864,6 +879,20 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) ret->cm_key_type.cm_key_gen_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(s[i], "ML-DSA-44") == 0) { + ret->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_44; + } else + if (strcasecmp(s[i], "ML-DSA-65") == 0) { + ret->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_65; + } else + if (strcasecmp(s[i], "ML-DSA-87") == 0) { + ret->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_87; +#endif } else { ret->cm_key_type.cm_key_gen_algorithm = cm_key_unspecified; @@ -895,6 +924,20 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) ret->cm_key_next_type.cm_key_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(s[i], "ML-DSA-44") == 0) { + ret->cm_key_next_type.cm_key_algorithm = + cm_key_ml_dsa_44; + } else + if (strcasecmp(s[i], "ML-DSA-65") == 0) { + ret->cm_key_next_type.cm_key_algorithm = + cm_key_ml_dsa_65; + } else + if (strcasecmp(s[i], "ML-DSA-87") == 0) { + ret->cm_key_next_type.cm_key_algorithm = + cm_key_ml_dsa_87; +#endif } else { ret->cm_key_next_type.cm_key_algorithm = cm_key_unspecified; @@ -918,6 +961,20 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) ret->cm_key_next_type.cm_key_gen_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(s[i], "ML-DSA-44") == 0) { + ret->cm_key_next_type.cm_key_gen_algorithm = + cm_key_ml_dsa_44; + } else + if (strcasecmp(s[i], "ML-DSA-65") == 0) { + ret->cm_key_next_type.cm_key_gen_algorithm = + cm_key_ml_dsa_65; + } else + if (strcasecmp(s[i], "ML-DSA-87") == 0) { + ret->cm_key_next_type.cm_key_gen_algorithm = + cm_key_ml_dsa_87; +#endif } else { ret->cm_key_next_type.cm_key_gen_algorithm = cm_key_unspecified; @@ -1830,6 +1887,20 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) "EC"); break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + cm_store_file_write_str(fp, cm_store_entry_field_key_type, + "ML-DSA-44"); + break; + case cm_key_ml_dsa_65: + cm_store_file_write_str(fp, cm_store_entry_field_key_type, + "ML-DSA-65"); + break; + case cm_key_ml_dsa_87: + cm_store_file_write_str(fp, cm_store_entry_field_key_type, + "ML-DSA-87"); + break; +#endif } switch (entry->cm_key_type.cm_key_gen_algorithm) { case cm_key_unspecified: @@ -1852,6 +1923,20 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) "EC"); break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + cm_store_file_write_str(fp, cm_store_entry_field_key_gen_type, + "ML-DSA-44"); + break; + case cm_key_ml_dsa_65: + cm_store_file_write_str(fp, cm_store_entry_field_key_gen_type, + "ML-DSA-65"); + break; + case cm_key_ml_dsa_87: + cm_store_file_write_str(fp, cm_store_entry_field_key_gen_type, + "ML-DSA-87"); + break; +#endif } cm_store_file_write_int(fp, cm_store_entry_field_key_size, entry->cm_key_type.cm_key_size); @@ -1878,6 +1963,20 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) "EC"); break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_type, + "ML-DSA-44"); + break; + case cm_key_ml_dsa_65: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_type, + "ML-DSA-65"); + break; + case cm_key_ml_dsa_87: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_type, + "ML-DSA-87"); + break; +#endif } switch (entry->cm_key_next_type.cm_key_gen_algorithm) { case cm_key_unspecified: @@ -1900,6 +1999,20 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) "EC"); break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_gen_type, + "ML-DSA-44"); + break; + case cm_key_ml_dsa_65: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_gen_type, + "ML-DSA-65"); + break; + case cm_key_ml_dsa_87: + cm_store_file_write_str(fp, cm_store_entry_field_key_next_gen_type, + "ML-DSA-87"); + break; +#endif } cm_store_file_write_int(fp, cm_store_entry_field_key_next_size, entry->cm_key_next_type.cm_key_size); @@ -2989,3 +3102,37 @@ cm_store_ca_dup(void *parent, struct cm_store_ca *ca) return ret; } + +char *cm_store_algorithm_to_name(int algorithm) +{ + /* See cm_key_algorithm in store-int.h */ + switch(algorithm) { + case cm_key_rsa: + return "RSA"; + break; +#ifdef CM_ENABLE_DSA + case cm_key_dsa: + return "DSA"; + break; +#endif +#ifdef CM_ENABLE_EC + case cm_key_ecdsa: + return "EC"; + break; +#endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + return "ML-DSA-44"; + break; + case cm_key_ml_dsa_65: + return "ML-DSA-65"; + break; + case cm_key_ml_dsa_87: + return "ML-DSA-87"; + break; +#endif + default: + return "UNKNOWN"; + break; + } +}; diff --git a/src/store-int.h b/src/store-int.h index 7557919..fca9877 100644 --- a/src/store-int.h +++ b/src/store-int.h @@ -40,6 +40,11 @@ struct cm_store_entry { #ifdef CM_ENABLE_EC cm_key_ecdsa, #endif +#ifdef CM_ENABLE_ML_DSA + cm_key_ml_dsa_44, + cm_key_ml_dsa_65, + cm_key_ml_dsa_87 +#endif } cm_key_algorithm, cm_key_gen_algorithm; int cm_key_size, cm_key_gen_size; } cm_key_type, cm_key_next_type; diff --git a/src/store.h b/src/store.h index 2c2b326..6781411 100644 --- a/src/store.h +++ b/src/store.h @@ -70,5 +70,6 @@ void cm_store_set_if_not_set_as(void *parent, char ***dest, char **src); int cm_store_utf8_to_bmp_string(char *s, unsigned char **bmp, unsigned int *len); char *cm_store_utf8_from_bmp_string(unsigned char *bmp, unsigned int len); +char *cm_store_algorithm_to_name(int algorithm); #endif diff --git a/src/submit-e.c b/src/submit-e.c index 69b4f8e..2e935e4 100644 --- a/src/submit-e.c +++ b/src/submit-e.c @@ -884,6 +884,17 @@ cm_submit_e_helper_main(int fd, struct cm_store_ca *ca, key_type = "EC"; break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + key_type = "ML-DSA-44"; + break; + case cm_key_ml_dsa_65: + key_type = "ML-DSA-65"; + break; + case cm_key_ml_dsa_87: + key_type = "ML-DSA-87"; + break; +#endif case cm_key_unspecified: key_type = NULL; break; diff --git a/src/tdbush.c b/src/tdbush.c index d440863..40bbf4a 100644 --- a/src/tdbush.c +++ b/src/tdbush.c @@ -1317,6 +1317,20 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, new_entry->cm_key_type.cm_key_gen_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(param->value.s, "ML-DSA-44") == 0) { + new_entry->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_44; + } else + if (strcasecmp(param->value.s, "ML-DSA-65") == 0) { + new_entry->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_65; + } else + if (strcasecmp(param->value.s, "ML-DSA-87") == 0) { + new_entry->cm_key_type.cm_key_gen_algorithm = + cm_key_ml_dsa_87; +#endif } else { cm_log(1, "No support for generating \"%s\" keys.\n", param->value.s); @@ -1362,6 +1376,16 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, } break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + if (new_entry->cm_key_type.cm_key_gen_size < CM_MINIMUM_ML_DSA_KEY_SIZE) + { + new_entry->cm_key_type.cm_key_gen_size = CM_MINIMUM_ML_DSA_KEY_SIZE; + } + break; +#endif case cm_key_unspecified: default: break; @@ -1838,6 +1862,11 @@ base_get_supported_key_types(DBusConnection *conn, DBusMessage *msg, #ifdef CM_ENABLE_EC "EC", #endif +#ifdef CM_ENABLE_ML_DSA + "ML-DSA-44", + "ML-DSA-65", + "ML-DSA-87", +#endif NULL }; DBusMessage *rep; @@ -2924,27 +2953,7 @@ request_get_key_type_and_size(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } rep = dbus_message_new_method_return(msg); - switch (entry->cm_key_type.cm_key_algorithm) { - case cm_key_unspecified: - type = "UNKNOWN"; - break; - case cm_key_rsa: - type = "RSA"; - break; -#ifdef CM_ENABLE_DSA - case cm_key_dsa: - type = "DSA"; - break; -#endif -#ifdef CM_ENABLE_EC - case cm_key_ecdsa: - type = "EC"; - break; -#endif - default: - type = "UNKNOWN"; - break; - } + type = cm_store_algorithm_to_name(entry->cm_key_type.cm_key_algorithm); if (rep != NULL) { size = entry->cm_key_type.cm_key_size; cm_tdbusm_set_sn(rep, type, size); @@ -3251,6 +3260,17 @@ request_modify(DBusConnection *conn, DBusMessage *msg, (strcasecmp(param->value.s, "EC") == 0)) { entry->cm_key_type.cm_key_gen_algorithm = cm_key_ecdsa; #endif +#ifdef CM_ENABLE_ML_DSA + } else + if (strcasecmp(param->value.s, "ML-DSA-44") == 0) { + entry->cm_key_type.cm_key_gen_algorithm = cm_key_ml_dsa_44; + } else + if (strcasecmp(param->value.s, "ML-DSA-65") == 0) { + entry->cm_key_type.cm_key_gen_algorithm = cm_key_ml_dsa_65; + } else + if (strcasecmp(param->value.s, "ML-DSA-87") == 0) { + entry->cm_key_type.cm_key_gen_algorithm = cm_key_ml_dsa_87; +#endif } else { cm_log(1, "No support for generating \"%s\" keys.\n", param->value.s); @@ -4049,6 +4069,17 @@ request_prop_get_key_type(struct cm_context *ctx, void *parent, return "EC"; break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + return "ML-DSA-44"; + break; + case cm_key_ml_dsa_65: + return "ML-DSA-65"; + break; + case cm_key_ml_dsa_87: + return "ML-DSA-87"; + break; +#endif } return ""; } @@ -4071,6 +4102,12 @@ request_prop_get_key_size(struct cm_context *ctx, void *parent, #ifdef CM_ENABLE_EC case cm_key_ecdsa: #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + /* fall through */ +#endif return entry->cm_key_type.cm_key_size; break; } diff --git a/tests/002-keygen-ml-dsa/expected.out b/tests/002-keygen-ml-dsa/expected.out new file mode 100644 index 0000000..b303959 --- /dev/null +++ b/tests/002-keygen-ml-dsa/expected.out @@ -0,0 +1,14 @@ +[openssl:44] +OK. +OK (ML-DSA-44:10496). +[openssl:65] +OK. +OK (ML-DSA-65:15616). +[openssl:87] +OK. +OK (ML-DSA-87:20736). +[openssl:rosubdir] +Failed to save FILE:${tmpdir}/rosubdir/sample.87: need fs permissions. +[openssl:rwsubdir] +Failed to save FILE:${tmpdir}/rwsubdir/sample.87: need fs permissions. +Test complete. diff --git a/tests/002-keygen-ml-dsa/run.sh b/tests/002-keygen-ml-dsa/run.sh new file mode 100755 index 0000000..55360f7 --- /dev/null +++ b/tests/002-keygen-ml-dsa/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash -e + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +for size in 44 65 87 ; do + echo "[openssl:$size]" + # Generate a key. + cat > entry.$size <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/sample.$size + key_gen_size=$size + key_gen_type=ML-DSA-$size + EOF + $toolsdir/keygen entry.$size + # Read the size. + sed -i 's,^key_gen_size.*,,g' entry.$size + $toolsdir/keyiread entry.$size +done + +echo "[openssl:rosubdir]" +cat > entry.$size <<- EOF +key_storage_type=FILE +key_storage_location=$tmpdir/rosubdir/sample.$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +echo "[openssl:rwsubdir]" +cat > entry.$size <<- EOF +key_storage_type=FILE +key_storage_location=$tmpdir/rwsubdir/sample.$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +touch $tmpdir/rwsubdir/sample.$size +chmod u-w $tmpdir/rwsubdir/sample.$size +$toolsdir/keygen entry.$size || true + +echo Test complete. diff --git a/tests/028-dbus/expected.out b/tests/028-dbus/expected.out index 86cba02..28bed7c 100644 --- a/tests/028-dbus/expected.out +++ b/tests/028-dbus/expected.out @@ -185,7 +185,7 @@ dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA1'), dbus.Object dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/requests/Request2')], signature=dbus.Signature('o')) [ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_types ] -dbus.Array([dbus.String('RSA'), dbus.String('DSA'), dbus.String('EC')], signature=dbus.Signature('s')) +dbus.Array([dbus.String('RSA'), dbus.String('DSA'), dbus.String('EC'), dbus.String('ML-DSA-44'), dbus.String('ML-DSA-65'), dbus.String('ML-DSA-87')], signature=dbus.Signature('s')) [ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_storage ] dbus.Array([dbus.String('NSSDB'), dbus.String('FILE')], signature=dbus.Signature('s')) @@ -712,7 +712,7 @@ dbus.Array([], signature=dbus.Signature('s')) [ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_config_file_path ] -$tmpdir/cas/20180327134236 +$tmpdir/cas/20250807195047 [ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_nickname ] SelfSign @@ -827,7 +827,7 @@ dbus.Array([], signature=dbus.Signature('s')) [ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_config_file_path ] -$tmpdir/cas/20180327134236-1 +$tmpdir/cas/20250807195047-1 [ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_nickname ] IPA @@ -940,7 +940,7 @@ dbus.Array([], signature=dbus.Signature('s')) [ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_config_file_path ] -$tmpdir/cas/20180327134236-3 +$tmpdir/cas/20250807195047-2 [ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_nickname ] dogtag-ipa-renew-agent diff --git a/tests/tools/keyiread.c b/tests/tools/keyiread.c index 5934124..0cbed52 100644 --- a/tests/tools/keyiread.c +++ b/tests/tools/keyiread.c @@ -64,6 +64,17 @@ type_name(enum cm_key_algorithm alg) return "EC"; break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + return "ML-DSA-44"; + break; + case cm_key_ml_dsa_65: + return "ML-DSA-65"; + break; + case cm_key_ml_dsa_87: + return "ML-DSA-87"; + break; +#endif default: assert(0); break; @@ -86,6 +97,12 @@ munge_key_size(struct cm_key_type *key) case cm_key_ecdsa: break; #endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + break; +#endif default: assert(0); break; From 281fb7ca25f1de11d8ae72f3122c82a67717300f Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 3/12] Add initial ML-DSA support with NSS 3.112.0-4 NSS 3.112 has added support for Post-Quantum ML-DSA keys. Allow them to be generated by certmonger. The minimum key size is not fully implemented because key size has a different meaning in this context. It is plumbed in, such that one can set a numeric size, but it isn't used and is effectively meaningless. This includes what may be a temporary change to identify the key type when reading a private key. NSS has an internal function for this that may be exposed in the future. This works for now. --- diff --git a/src/keygen-n.c b/src/keygen-n.c index 3c3b6cd..14b7c7d 100644 --- a/src/keygen-n.c +++ b/src/keygen-n.c @@ -140,6 +140,9 @@ cm_keygen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, SECOidData *ecurve; SECItem ec_params; #endif +#ifdef CM_ENABLE_ML_DSA + CK_ULONG ml_params; +#endif SECKEYPrivateKey *privkey, *delkey, *ckey; SECKEYPrivateKeyList *privkeys; SECKEYPrivateKeyListNode *node; @@ -273,8 +276,15 @@ cm_keygen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, #endif #ifdef CM_ENABLE_ML_DSA: case cm_key_ml_dsa_44: + cm_requested_key_size = ML_DSA_44_PRIVATEKEY_LEN; + mech = CKM_ML_DSA_KEY_PAIR_GEN; + pmech = CKM_ML_DSA_KEY_PAIR_GEN; case cm_key_ml_dsa_65: + cm_requested_key_size = ML_DSA_65_PRIVATEKEY_LEN; + mech = CKM_ML_DSA_KEY_PAIR_GEN; + pmech = CKM_ML_DSA_KEY_PAIR_GEN; case cm_key_ml_dsa_87: + cm_requested_key_size = ML_DSA_87_PRIVATEKEY_LEN; mech = CKM_ML_DSA_KEY_PAIR_GEN; pmech = CKM_ML_DSA_KEY_PAIR_GEN; break; @@ -330,6 +340,8 @@ next_slot: cm_log(1, "Error locating token for key generation.\n"); _exit(CM_SUB_STATUS_ERROR_NO_TOKEN); } + /* FIXME */ + cm_log(1, "Selected token %s\n", PK11_GetTokenName(slot)); /* Be ready to count our uses of a PIN. */ memset(&cb_data, 0, sizeof(cb_data)); cb_data.entry = entry; @@ -582,9 +594,19 @@ retry_gen: #endif #ifdef CM_ENABLE_ML_DSA: case cm_key_ml_dsa_44: + memset(&ml_params, 0, sizeof(ml_params)); + ml_params = CKP_ML_DSA_44; + params = &ml_params; + break; case cm_key_ml_dsa_65: + memset(&ml_params, 0, sizeof(ml_params)); + ml_params = CKP_ML_DSA_65; + params = &ml_params; + break; case cm_key_ml_dsa_87: - /* FIXME */ + memset(&ml_params, 0, sizeof(ml_params)); + ml_params = CKP_ML_DSA_87; + params = &ml_params; break; #endif default: @@ -592,10 +614,11 @@ retry_gen: break; } /* Generate the key pair. */ - cm_log(1, "Generating key pair.\n"); + cm_log(1, "Generating key pair of size %d.\n", cm_key_size); pubkey = NULL; privkey = PK11_GenerateKeyPair(slot, mech, params, &pubkey, PR_TRUE, PR_TRUE, NULL); + /* Retry with the optimum key size. */ if (privkey == NULL) { cm_key_size = PK11_GetBestKeyLength(slot, pmech); diff --git a/src/keyiread-n.c b/src/keyiread-n.c index ed87b09..1f08840 100644 --- a/src/keyiread-n.c +++ b/src/keyiread-n.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,37 @@ #define PRIVKEY_LIST_EMPTY(l) PRIVKEY_LIST_END(PRIVKEY_LIST_HEAD(l), l) #endif +const char * +cm_GetSignatureAlgorithmFromPrivateKey(SECKEYPrivateKey *privKey) +{ + SECOidTag hashAlgTag = SEC_OID_UNKNOWN; + SECOidTag enctag = SEC_GetSignatureAlgorithmOidTagByKey(privKey, NULL, hashAlgTag); + + if (privKey->keyType != mldsaKey) { + /* should not get here */ + return "Invalid ML-DSA key"; + } + + if (enctag == SEC_OID_UNKNOWN) { + return "UNKNOWN"; + } + switch (enctag) { + case SEC_OID_ML_DSA_44_PUBLIC_KEY: + return "ML-DSA-44"; + break; + case SEC_OID_ML_DSA_65_PUBLIC_KEY: + return "ML-DSA-65"; + break; + case SEC_OID_ML_DSA_87_PUBLIC_KEY: + return "ML-DSA-87"; + break; + default: + return "UNKNOWN"; + break; + } + return "UNKNOWN"; +} + struct cm_keyiread_state { struct cm_keyiread_state_pvt pvt; struct cm_store_entry *entry; @@ -522,7 +554,10 @@ cm_keyiread_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, cm_log(3, "Key is an EC key.\n"); alg = "EC"; break; - /* FIXME: case mlKey? */ + case mldsaKey: + cm_log(3, "Key is an ML-DSA key.\n"); + alg = cm_GetSignatureAlgorithmFromPrivateKey(keys->privkey); + break; case nullKey: default: cm_log(3, "Key is of an unknown type.\n"); diff --git a/src/keyiread.c b/src/keyiread.c index 668b6d3..66b527d 100644 --- a/src/keyiread.c +++ b/src/keyiread.c @@ -157,7 +157,6 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) #endif } else { alg = cm_key_unspecified; - cm_log(1, "debug: determined unspcified from %d\n", alg); } if (alg != cm_key_unspecified) { p = q + strspn(q, "/\r\n"); @@ -165,7 +164,6 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) if (p != q) { size = atoi(p); if (size > 0) { - cm_log(1, "debug: setting alg %d size %d\n", alg, size); entry->cm_key_type.cm_key_algorithm = alg; entry->cm_key_type.cm_key_size = size; if (entry->cm_key_type.cm_key_gen_algorithm == 0) { @@ -233,7 +231,6 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) alg = cm_key_ml_dsa_87; #endif } else { - cm_log(1, "debug: setting next unspcified from %s\n", p); alg = cm_key_unspecified; } if (alg != cm_key_unspecified) { @@ -260,5 +257,16 @@ cm_keyiread_read_data_from_buffer(struct cm_store_entry *entry, const char *p) p, q - p); } } + } else { + /* Next type not provided or incomplete: default next algorithm to + * the current key's algorithm. Only sync gen from algorithm when + * the current key type is known; otherwise a stub reader line + * (no key file yet) would clear gen and force default RSA keygen. */ + entry->cm_key_next_type.cm_key_algorithm = + entry->cm_key_type.cm_key_algorithm; + if (entry->cm_key_type.cm_key_algorithm != cm_key_unspecified) { + entry->cm_key_type.cm_key_gen_algorithm = + entry->cm_key_type.cm_key_algorithm; + } } } diff --git a/tests/002-keygen-ml-dsa/expected.out b/tests/002-keygen-ml-dsa/expected.out index b303959..88da9f1 100644 --- a/tests/002-keygen-ml-dsa/expected.out +++ b/tests/002-keygen-ml-dsa/expected.out @@ -1,3 +1,16 @@ +[nss:44] +OK. +OK (ML-DSA-44:10496). +[nss:65] +OK. +OK (ML-DSA-65:15616). +[nss:87] +OK. +OK (ML-DSA-87:20736). +[nss:rosubdir] +Failed to save NSS:${tmpdir}/rosubdir: need fs permissions. +[nss:rwsubdir] +Failed to save NSS:${tmpdir}/rwsubdir: need fs permissions. [openssl:44] OK. OK (ML-DSA-44:10496). diff --git a/tests/002-keygen-ml-dsa/prequal.sh b/tests/002-keygen-ml-dsa/prequal.sh new file mode 100644 index 0000000..d146a65 --- /dev/null +++ b/tests/002-keygen-ml-dsa/prequal.sh @@ -0,0 +1,5 @@ +#!/bin/sh +if test `id -u` -eq 0 ; then + echo "This test won't work right if run as root." + exit 1 +fi diff --git a/tests/002-keygen-ml-dsa/run.sh b/tests/002-keygen-ml-dsa/run.sh index 55360f7..6adc541 100755 --- a/tests/002-keygen-ml-dsa/run.sh +++ b/tests/002-keygen-ml-dsa/run.sh @@ -6,6 +6,42 @@ source "$srcdir"/functions initnssdb "$tmpdir" for size in 44 65 87 ; do + echo "[nss:$size]" + # Generate a key. + cat > entry.$size <<- EOF + key_storage_type=NSSDB + key_storage_location=$tmpdir + key_nickname=keyi$size + key_gen_size=$size + key_gen_type=ML-DSA-$size + EOF + $toolsdir/keygen entry.$size + # Read the type and size. + sed -i 's,^key_gen_size.*,,g' entry.$size + $toolsdir/keyiread entry.$size +done + +echo "[nss:rosubdir]" +cat > entry.$size <<- EOF +key_storage_type=NSSDB +key_storage_location=$tmpdir/rosubdir +key_nickname=keyi$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +echo "[nss:rwsubdir]" +cat > entry.$size <<- EOF +key_storage_type=NSSDB +key_storage_location=$tmpdir/rwsubdir +key_nickname=keyi$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +for size in 44 65 87 ; do echo "[openssl:$size]" # Generate a key. cat > entry.$size <<- EOF From 0fab580622f58b1c6d9c00f87c8de7b22c8013e2 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 4/12] Add helper to get the generated key size This was always defaulting to RSA. Based it on key algorithm. --- diff --git a/src/prefs.c b/src/prefs.c index 917a9f4..c53f79b 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -551,6 +551,34 @@ cm_prefs_preferred_rsa_key_size(void) return size; } +int +cm_prefs_default_key_gen_size(enum cm_key_algorithm gen_algorithm) +{ + switch (gen_algorithm) { + case cm_key_rsa: + return cm_prefs_preferred_rsa_key_size(); +#ifdef CM_ENABLE_DSA + case cm_key_dsa: + return CM_MINIMUM_DSA_KEY_SIZE; +#endif +#ifdef CM_ENABLE_EC + case cm_key_ecdsa: + return CM_MINIMUM_EC_KEY_SIZE; +#endif +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + return 10496; + case cm_key_ml_dsa_65: + return 15616; + case cm_key_ml_dsa_87: + return 20736; +#endif + case cm_key_unspecified: + default: + return cm_prefs_preferred_rsa_key_size(); + } +} + const char * cm_prefs_nss_ca_trust(void) { diff --git a/src/prefs.h b/src/prefs.h index a107fb6..1b3cfee 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -46,6 +46,7 @@ enum cm_cert_storage_type; enum cm_key_algorithm cm_prefs_preferred_key_algorithm(void); int cm_prefs_preferred_rsa_key_size(void); +int cm_prefs_default_key_gen_size(enum cm_key_algorithm gen_algorithm); enum cm_prefs_cipher cm_prefs_preferred_cipher(void); enum cm_prefs_digest cm_prefs_preferred_digest(void); int cm_prefs_notify_ttls(const time_t **ttls, unsigned int *n_ttls); From d67c5dda8d8468c04098f45c79db4d295eca1bbc Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 5/12] Test for PQ support in NSS, print summary at end of configure Add test to see if PQ is supported by NSS. If both it and OpenSSL have support then enable it in certmonger. Print a summary of directories and enabled ciphers at the end of configure. --- diff --git a/configure.ac b/configure.ac index ecc35e1..b1a28ed 100644 --- a/configure.ac +++ b/configure.ac @@ -724,14 +724,17 @@ if ! ${configure_dist_target_only:-false} ; then CFLAGS="$OPENSSL_CFLAGS $CFLAGS" LIBS="$OPENSSL_LIBS $LIBS" AC_CHECK_LIB(crypto,EVP_PKEY_Q_keygen) - # CFLAGS="$NSS_CFLAGS $CFLAGS" - # LIBS="$NSS_LIBS $LIBS" - # AC_CHECK_FUNCS(SECKEY_CreateECPrivateKey) - # AC_CHECK_FUNCS(PK11_PrivDecrypt) - # AC_CHECK_FUNCS(NSS_OptionGet NSS_OptionSet) + CFLAGS="$NSS_CFLAGS $CFLAGS" + LIBS="$NSS_LIBS $LIBS" + AC_CHECK_TYPES([CKM_ML_DSA_KEY_PAIR_GEN],,,[[#include ]]) + AC_CHECK_FUNCS(PK11_GenerateKeyPair) + AC_CHECK_FUNCS(NSS_OptionGet NSS_OptionSet) CFLAGS="$CFLAGSsave" LIBS="$LIBSsave" can_ml_dsa=true + if test x$HAVE_CKM_ML_DSA_KEY_PAIR_GEN = xno ; then + can_ml_dsa=false + fi if test x$ac_cv_lib_crypto_EVP_PKEY_Q_keygen = xno ; then can_ml_dsa=false fi @@ -944,4 +947,47 @@ else fi AC_CONFIG_COMMANDS(src_introspect_sh,[chmod +x src/introspect.sh]) -AC_OUTPUT(Makefile src/Makefile dbus/Makefile systemd/Makefile sysvinit/Makefile sysvinit/certmonger tests/Makefile tests/tools/Makefile dbus/org.fedorahosted.certmonger.conf dbus/org.fedorahosted.certmonger.service src/introspect.sh src/certmonger.8 src/getcert.1 src/getcert-request.1 src/getcert-list.1 src/getcert-list-cas.1 src/getcert-start-tracking.1 src/getcert-stop-tracking.1 src/selfsign-getcert.1 src/ipa-getcert.1 src/getcert-resubmit.1 src/certmonger-certmaster-submit.8 src/certmonger-ipa-submit.8 src/certmonger-dogtag-ipa-renew-agent-submit.8 src/certmaster-getcert.1 src/certmonger.conf.5 po/Makefile.in src/certmonger.conf systemd/certmonger.service systemd/certmonger.conf systemd/org.fedorahosted.certmonger.service src/getcert-refresh.1 src/getcert-refresh-ca.1 src/local-getcert.1 src/certmonger-local-submit.8 src/getcert-status.1 src/certmonger-dogtag-submit.8 src/certmonger-scep-submit.8 src/getcert-add-ca.1 src/getcert-add-scep-ca.1 src/getcert-modify-ca.1 src/getcert-remove-ca.1 src/getcert-rekey.1) +AC_CONFIG_FILES([Makefile src/Makefile dbus/Makefile systemd/Makefile sysvinit/Makefile sysvinit/certmonger tests/Makefile tests/tools/Makefile dbus/org.fedorahosted.certmonger.conf dbus/org.fedorahosted.certmonger.service src/introspect.sh src/certmonger.8 src/getcert.1 src/getcert-request.1 src/getcert-list.1 src/getcert-list-cas.1 src/getcert-start-tracking.1 src/getcert-stop-tracking.1 src/selfsign-getcert.1 src/ipa-getcert.1 src/getcert-resubmit.1 src/certmonger-certmaster-submit.8 src/certmonger-ipa-submit.8 src/certmonger-dogtag-ipa-renew-agent-submit.8 src/certmaster-getcert.1 src/certmonger.conf.5 po/Makefile.in src/certmonger.conf systemd/certmonger.service systemd/certmonger.conf systemd/org.fedorahosted.certmonger.service src/getcert-refresh.1 src/getcert-refresh-ca.1 src/local-getcert.1 src/certmonger-local-submit.8 src/getcert-status.1 src/certmonger-dogtag-submit.8 src/certmonger-scep-submit.8 src/getcert-add-ca.1 src/getcert-add-scep-ca.1 src/getcert-modify-ca.1 src/getcert-remove-ca.1 src/getcert-rekey.1]) + +AC_OUTPUT + +echo " + prefix: ${prefix} + exec_prefix: ${exec_prefix} + libdir: ${libdir} + bindir: ${bindir} + sbindir: ${sbindir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} + datadir: ${datadir} + source code location: ${srcdir} + compiler: ${CC} + cflags: ${CFLAGS} + LDAP libs: ${LDAP_LIBS} + OpenSSL crypto libs: ${OPENSSL_LIBS} + NSS libs: ${NSS_LIBS} + JANSSON libs: ${JANSSON_LIBS}" + +AM_COND_IF([HAVE_DSA], [ + echo "\ + DSA support enabled: yes" +], [ + echo "\ + DSA support enabled: no" +]) + +AM_COND_IF([HAVE_EC], [ + echo "\ + EC support enabled: yes" +], [ + echo "\ + EC support enabled: no" +]) + +AM_COND_IF([HAVE_ML_DSA], [ + echo "\ + ML-DSA support enabled: yes" +], [ + echo "\ + ML-DSA support enabled: no" +]) From c81d5fff3e4460f4e38dc81037ae23991b17c560 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 6/12] Implement more PQ testing for both NSS and OpenSSL This adds keyiread and csrgen tests for both NSS and OpenSSL. keyiread, keygen and csrgen will now run by default when PQ is enabled. It also fixes the 028-dbus test if PQ is not enabled either by configuration (--disable-ml-dsa) or because it isn't available. --- diff --git a/src/csrgen-o.c b/src/csrgen-o.c index f1091c5..e1f8c4a 100644 --- a/src/csrgen-o.c +++ b/src/csrgen-o.c @@ -191,6 +191,7 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, unsigned int bmpcount, mdlen; long error; int i; + const EVP_MD *sign_md = cm_prefs_ossl_sig_alg(entry->cm_key_type.cm_key_algorithm); status = fdopen(fd, "w"); if (status == NULL) { @@ -344,8 +345,15 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, upassword, strlen(password)); } - X509_REQ_sign(req, pkey, cm_prefs_ossl_hash()); - PEM_write_X509_REQ(status, req); + + if (X509_REQ_sign(req, pkey, sign_md) == 0) { + cm_log_errors(1, "Error signing the CSR.\n"); + _exit(CM_SUB_STATUS_ERROR_AUTH); /* XXX */ + } + if (PEM_write_X509_REQ(status, req) != 1) { + cm_log_errors(1, "Error while writing the CSR PEM.\n"); + _exit(CM_SUB_STATUS_ERROR_AUTH); /* XXX */ + } /* Generate the SPKAC. */ memset(&spkac, 0, sizeof(spkac)); spkac.challenge = util_ASN1_IA5STRING_new(); @@ -362,7 +370,9 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, util_NETSCAPE_SPKI_set_sig_alg(&spki, sig_alg); spki.signature = util_ASN1_BIT_STRING_new(); NETSCAPE_SPKI_set_pubkey(&spki, pkey); - NETSCAPE_SPKI_sign(&spki, pkey, cm_prefs_ossl_hash()); + if (NETSCAPE_SPKI_sign(&spki, pkey, sign_md) == 0) { + cm_log_errors(1, "NETSCAPE_SPKI_sign failed.\n"); + } s = NETSCAPE_SPKI_b64_encode(&spki); if (s != NULL) { fprintf(status, "%s", s); @@ -429,7 +439,9 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, } X509_set_serialNumber(minicert, serial); X509_set_pubkey(minicert, pkey); - X509_sign(minicert, pkey, cm_prefs_ossl_hash()); + if (X509_sign(minicert, pkey, sign_md) == 0) { + cm_log_errors(1, "Signing minicert failed.\n"); + } len = i2d_X509(minicert, NULL); mcb64 = NULL; if (len > 0) { diff --git a/src/prefs-n.c b/src/prefs-n.c index 584bdd7..d080f0a 100644 --- a/src/prefs-n.c +++ b/src/prefs-n.c @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include "prefs.h" @@ -114,6 +116,45 @@ cm_prefs_nss_sig_alg(SECKEYPrivateKey *pkey) return SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break; } +#ifdef CM_ENABLE_ML_DSA + case mldsaKey: + /* This is a simplified version of + * cm_GetSignatureAlgorithmFromPrivateKey from keyiread-n.c. + * Once the NSS function is exported that can be moved to + * something like util-n.c and be generalized. For now I + * duplicate. + */ + SECItem item; + CK_ULONG paramSet; + SECStatus rv; + + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, pkey, CKA_PARAMETER_SET, &item); + if (rv != SECSuccess) { + return SEC_OID_UNKNOWN; + } + + if (item.len != sizeof(paramSet)) { + PORT_Free(item.data); + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SEC_OID_UNKNOWN; + } + paramSet = *(CK_ULONG *)item.data; + PORT_Free(item.data); + switch (paramSet) { + case CKP_ML_DSA_44: + return SEC_OID_ML_DSA_44_SIGNATURE; + break; + case CKP_ML_DSA_65: + return SEC_OID_ML_DSA_65_SIGNATURE; + break; + case CKP_ML_DSA_87: + return SEC_OID_ML_DSA_87_SIGNATURE; + break; + default: + return SEC_OID_UNKNOWN; + break; + } +#endif default: return SEC_OID_UNKNOWN; break; diff --git a/src/prefs-o.c b/src/prefs-o.c index 4b6ef6f..83d50f2 100644 --- a/src/prefs-o.c +++ b/src/prefs-o.c @@ -32,6 +32,25 @@ #include "prefs.h" #include "prefs-o.h" #include "util-o.h" +#include "log.h" +#include "store-int.h" + +const EVP_MD * +cm_prefs_ossl_sig_alg(enum cm_key_algorithm cm_key_algorithm) +{ + switch(cm_key_algorithm) { +#ifdef CM_ENABLE_ML_DSA + case cm_key_ml_dsa_44: + case cm_key_ml_dsa_65: + case cm_key_ml_dsa_87: + return NULL; /* Pure mode does not use hash then sign */ + break; +#endif + default: + return cm_prefs_ossl_hash_by_pref(cm_prefs_preferred_digest()); + break; + } +} const EVP_MD * cm_prefs_ossl_hash_by_pref(enum cm_prefs_digest digest) diff --git a/src/prefs-o.h b/src/prefs-o.h index 1dcd4e8..9c233ab 100644 --- a/src/prefs-o.h +++ b/src/prefs-o.h @@ -18,6 +18,7 @@ #ifndef cmprefso_h #define cmprefso_h +const EVP_MD * cm_prefs_ossl_sig_alg(enum cm_key_algorithm cm_key_algorithm); const EVP_CIPHER *cm_prefs_ossl_cipher(void); const EVP_CIPHER *cm_prefs_ossl_cipher_by_pref(enum cm_prefs_cipher cipher); const EVP_MD *cm_prefs_ossl_hash(void); diff --git a/tests/001-keyiread-mldsa/expected.out b/tests/001-keyiread-mldsa/expected.out new file mode 100644 index 0000000..82ab39a --- /dev/null +++ b/tests/001-keyiread-mldsa/expected.out @@ -0,0 +1,7 @@ +OK (ML-DSA-44:10496). +OK (ML-DSA-44:10496). +OK (ML-DSA-65:15616). +OK (ML-DSA-65:15616). +OK (ML-DSA-87:20736). +OK (ML-DSA-87:20736). +Test complete. diff --git a/tests/001-keyiread-mldsa/run.sh b/tests/001-keyiread-mldsa/run.sh new file mode 100755 index 0000000..9c01c2f --- /dev/null +++ b/tests/001-keyiread-mldsa/run.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +for size in ML-DSA-44 ML-DSA-65 ML-DSA-87 ; do + # Generate a self-signed cert. + run_certutil -d "$tmpdir" -S -n keyi$size \ + -s "cn=T$size" -c "cn=T$size" \ + -x -t u -k mldsa -q $size + # Check the size of the key. + cat > entry.nss.$size <<- EOF + key_storage_type=NSSDB + key_storage_location=$tmpdir + key_nickname=keyi$size + EOF + $toolsdir/keyiread entry.nss.$size + # Export the key. + if ! pk12util -C AES-128-CBC -c AES-128-CBC -d "$tmpdir" -o $size.p12 -W "" -n "keyi$size" > /dev/null 2>&1 ; then + echo Error exporting key for $size, continuing. + continue + fi + if ! openssl pkcs12 -in $size.p12 -out key.$size -passin pass: -nodes -nocerts > /dev/null 2>&1 ; then + echo Error parsing exported key for $size, continuing. + continue + fi + cat > entry.openssl.$size <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/key.$size + key_nickname=keyi$size + EOF + $toolsdir/keyiread entry.openssl.$size +done +echo Test complete. diff --git a/tests/002-keygen-ml-dsa/expected.out b/tests/002-keygen-ml-dsa/expected.out deleted file mode 100644 index 88da9f1..0000000 --- a/tests/002-keygen-ml-dsa/expected.out +++ /dev/null @@ -1,27 +0,0 @@ -[nss:44] -OK. -OK (ML-DSA-44:10496). -[nss:65] -OK. -OK (ML-DSA-65:15616). -[nss:87] -OK. -OK (ML-DSA-87:20736). -[nss:rosubdir] -Failed to save NSS:${tmpdir}/rosubdir: need fs permissions. -[nss:rwsubdir] -Failed to save NSS:${tmpdir}/rwsubdir: need fs permissions. -[openssl:44] -OK. -OK (ML-DSA-44:10496). -[openssl:65] -OK. -OK (ML-DSA-65:15616). -[openssl:87] -OK. -OK (ML-DSA-87:20736). -[openssl:rosubdir] -Failed to save FILE:${tmpdir}/rosubdir/sample.87: need fs permissions. -[openssl:rwsubdir] -Failed to save FILE:${tmpdir}/rwsubdir/sample.87: need fs permissions. -Test complete. diff --git a/tests/002-keygen-ml-dsa/prequal.sh b/tests/002-keygen-ml-dsa/prequal.sh deleted file mode 100644 index d146a65..0000000 --- a/tests/002-keygen-ml-dsa/prequal.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -if test `id -u` -eq 0 ; then - echo "This test won't work right if run as root." - exit 1 -fi diff --git a/tests/002-keygen-ml-dsa/run.sh b/tests/002-keygen-ml-dsa/run.sh deleted file mode 100755 index 6adc541..0000000 --- a/tests/002-keygen-ml-dsa/run.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -e - -cd "$tmpdir" - -source "$srcdir"/functions -initnssdb "$tmpdir" - -for size in 44 65 87 ; do - echo "[nss:$size]" - # Generate a key. - cat > entry.$size <<- EOF - key_storage_type=NSSDB - key_storage_location=$tmpdir - key_nickname=keyi$size - key_gen_size=$size - key_gen_type=ML-DSA-$size - EOF - $toolsdir/keygen entry.$size - # Read the type and size. - sed -i 's,^key_gen_size.*,,g' entry.$size - $toolsdir/keyiread entry.$size -done - -echo "[nss:rosubdir]" -cat > entry.$size <<- EOF -key_storage_type=NSSDB -key_storage_location=$tmpdir/rosubdir -key_nickname=keyi$size -key_gen_size=$size -key_gen_type=ML-DSA-$size -EOF -$toolsdir/keygen entry.$size || true - -echo "[nss:rwsubdir]" -cat > entry.$size <<- EOF -key_storage_type=NSSDB -key_storage_location=$tmpdir/rwsubdir -key_nickname=keyi$size -key_gen_size=$size -key_gen_type=ML-DSA-$size -EOF -$toolsdir/keygen entry.$size || true - -for size in 44 65 87 ; do - echo "[openssl:$size]" - # Generate a key. - cat > entry.$size <<- EOF - key_storage_type=FILE - key_storage_location=$tmpdir/sample.$size - key_gen_size=$size - key_gen_type=ML-DSA-$size - EOF - $toolsdir/keygen entry.$size - # Read the size. - sed -i 's,^key_gen_size.*,,g' entry.$size - $toolsdir/keyiread entry.$size -done - -echo "[openssl:rosubdir]" -cat > entry.$size <<- EOF -key_storage_type=FILE -key_storage_location=$tmpdir/rosubdir/sample.$size -key_gen_size=$size -key_gen_type=ML-DSA-$size -EOF -$toolsdir/keygen entry.$size || true - -echo "[openssl:rwsubdir]" -cat > entry.$size <<- EOF -key_storage_type=FILE -key_storage_location=$tmpdir/rwsubdir/sample.$size -key_gen_size=$size -key_gen_type=ML-DSA-$size -EOF -touch $tmpdir/rwsubdir/sample.$size -chmod u-w $tmpdir/rwsubdir/sample.$size -$toolsdir/keygen entry.$size || true - -echo Test complete. diff --git a/tests/002-keygen-mldsa/expected.out b/tests/002-keygen-mldsa/expected.out new file mode 100644 index 0000000..88da9f1 --- /dev/null +++ b/tests/002-keygen-mldsa/expected.out @@ -0,0 +1,27 @@ +[nss:44] +OK. +OK (ML-DSA-44:10496). +[nss:65] +OK. +OK (ML-DSA-65:15616). +[nss:87] +OK. +OK (ML-DSA-87:20736). +[nss:rosubdir] +Failed to save NSS:${tmpdir}/rosubdir: need fs permissions. +[nss:rwsubdir] +Failed to save NSS:${tmpdir}/rwsubdir: need fs permissions. +[openssl:44] +OK. +OK (ML-DSA-44:10496). +[openssl:65] +OK. +OK (ML-DSA-65:15616). +[openssl:87] +OK. +OK (ML-DSA-87:20736). +[openssl:rosubdir] +Failed to save FILE:${tmpdir}/rosubdir/sample.87: need fs permissions. +[openssl:rwsubdir] +Failed to save FILE:${tmpdir}/rwsubdir/sample.87: need fs permissions. +Test complete. diff --git a/tests/002-keygen-mldsa/prequal.sh b/tests/002-keygen-mldsa/prequal.sh new file mode 100644 index 0000000..d146a65 --- /dev/null +++ b/tests/002-keygen-mldsa/prequal.sh @@ -0,0 +1,5 @@ +#!/bin/sh +if test `id -u` -eq 0 ; then + echo "This test won't work right if run as root." + exit 1 +fi diff --git a/tests/002-keygen-mldsa/run.sh b/tests/002-keygen-mldsa/run.sh new file mode 100755 index 0000000..6631717 --- /dev/null +++ b/tests/002-keygen-mldsa/run.sh @@ -0,0 +1,84 @@ +#!/bin/bash -e + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +for size in 44 65 87 ; do + echo "[nss:$size]" + # Generate a key. + cat > entry.$size <<- EOF + key_storage_type=NSSDB + key_storage_location=$tmpdir + key_nickname=keyi$size + key_gen_size=$size + key_gen_type=ML-DSA-$size + EOF + $toolsdir/keygen entry.$size + # Read the type and size. + sed -i 's,^key_gen_size.*,,g' entry.$size + $toolsdir/keyiread entry.$size +done + +echo "[nss:rosubdir]" +cat > entry.$size <<- EOF +key_storage_type=NSSDB +key_storage_location=$tmpdir/rosubdir +key_nickname=keyi$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +echo "[nss:rwsubdir]" +cat > entry.$size <<- EOF +key_storage_type=NSSDB +key_storage_location=$tmpdir/rwsubdir +key_nickname=keyi$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +for size in 44 65 87 ; do + echo "[openssl:$size]" + # Generate a key. + cat > entry.$size <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/sample.$size + key_gen_size=$size + key_gen_type=ML-DSA-$size + EOF + $toolsdir/keygen entry.$size + # Read the size. + sed -i 's,^key_gen_size.*,,g' entry.$size + $toolsdir/keyiread entry.$size +done + +echo "[openssl:rosubdir]" +cat > entry.$size <<- EOF +key_storage_type=FILE +key_storage_location=$tmpdir/rosubdir/sample.$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +$toolsdir/keygen entry.$size || true + +echo "[openssl:rwsubdir]" +cat > entry.$size <<- EOF +key_storage_type=FILE +key_storage_location=$tmpdir/rwsubdir/sample.$size +key_gen_size=$size +key_gen_type=ML-DSA-$size +EOF +touch $tmpdir/rwsubdir/sample.$size +chmod u-w $tmpdir/rwsubdir/sample.$size +$toolsdir/keygen entry.$size || true + +echo Test complete. diff --git a/tests/003-csrgen-mldsa/expected.out b/tests/003-csrgen-mldsa/expected.out new file mode 100644 index 0000000..ed22729 --- /dev/null +++ b/tests/003-csrgen-mldsa/expected.out @@ -0,0 +1,7 @@ +verify OK +verify OK +Signature OK +Signature OK +minicert.openssl.ML-DSA-65.pem: OK +minicert.nss.ML-DSA-65.pem: OK +Test complete. diff --git a/tests/003-csrgen-mldsa/run.sh b/tests/003-csrgen-mldsa/run.sh new file mode 100755 index 0000000..0dc0720 --- /dev/null +++ b/tests/003-csrgen-mldsa/run.sh @@ -0,0 +1,52 @@ +#!/bin/bash -e + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +size=ML-DSA-65 + +# Build a self-signed certificate. +run_certutil -d "$tmpdir" -S -n keyi$size \ + -s "cn=T$size" -c "cn=T$size" \ + -x -t u -k mldsa -q $size +# Export the key. +pk12util -C AES-128-CBC -c AES-128-CBC -d "$tmpdir" -o $size.p12 -W "" -n "keyi$size" > /dev/null 2>&1 +openssl pkcs12 -in $size.p12 -out key.$size -passin pass: -nodes -nocerts > /dev/null 2>&1 | ( grep -v '^MAC verified OK$' || : ) +# Read the public key and cache it. +cat > entry.openssl.$size <<- EOF +key_storage_type=FILE +key_storage_location=$tmpdir/key.$size +key_nickname=keyi$size +id=keyi$size +EOF +$toolsdir/keyiread entry.openssl.$size > /dev/null 2>&1 +# Add the cached value to the prepping for the NSS copy. +cat > entry.nss.$size <<- EOF +key_storage_type=NSSDB +key_storage_location=$tmpdir +key_nickname=keyi$size +id=keyi$size +EOF +# Generate a new CSR for that certificate's key. +$toolsdir/csrgen entry.nss.$size > csr.nss.$size +grep ^spkac= entry.nss.$size | sed s,spkac,SPKAC, > spkac.nss.$size +# Generate a new CSR using the extracted key. +$toolsdir/csrgen entry.openssl.$size > csr.openssl.$size +grep ^spkac= entry.openssl.$size | sed s,spkac,SPKAC, > spkac.openssl.$size +# Pull out the mini-cert. +grep ^minicert= entry.openssl.$size | sed s,^minicert=,, | base64 -d > minicert.openssl.$size +openssl x509 -out minicert.openssl.$size.pem -in minicert.openssl.$size -inform der +grep ^minicert= entry.nss.$size | sed s,^minicert=,, | base64 -d > minicert.nss.$size +openssl x509 -out minicert.nss.$size.pem -in minicert.nss.$size -inform der +# The RSA tests already verify the contents of the requests, so we really only +# need to care about the signatures passing verification. +openssl req -verify -noout -in csr.nss.$size 2>&1 | sed 's/Certificate request self-signature //' +openssl req -verify -noout -in csr.openssl.$size 2>&1 | sed 's/Certificate request self-signature //' +openssl spkac -verify -noout < spkac.nss.$size 2>&1 +openssl spkac -verify -noout < spkac.openssl.$size 2>&1 +openssl verify -CAfile minicert.openssl.$size.pem minicert.openssl.$size.pem 2>&1 +openssl verify -CAfile minicert.nss.$size.pem minicert.nss.$size.pem 2>&1 + +echo Test complete. diff --git a/tests/028-dbus/expected.out.nodsa.withmldsa b/tests/028-dbus/expected.out.nodsa.withmldsa new file mode 100644 index 0000000..99d9356 --- /dev/null +++ b/tests/028-dbus/expected.out.nodsa.withmldsa @@ -0,0 +1,965 @@ +Certificate in file "${tmpdir}/test.crt" issued by CA and saved. +Certificate in file "${tmpdir}/test.crt" issued by CA and saved. +[[ getcert ]] +State MONITORING, stuck: no. +Number of certificates and requests being tracked: 1. +Request ID 'Buddy': + status: MONITORING + stuck: no + key pair storage: type=FILE,location='$tmpdir/test.key' + certificate: type=FILE,location='$tmpdir/test.crt' + CA: local + issuer: CN=$UUID,CN=Local Signing Authority + subject: CN=localhost + issued: sometime + expires: sometime + dns: localhost + principal name: host/localhost@LOCALHOST + key usage: digitalSignature,dataEncipherment + eku: id-kp-serverAuth + certificate template/profile: SomeProfileName + profile: SomeProfileName + pre-save command: echo Pre + post-save command: echo Post + track: yes + auto-renew: yes +CA 'local': + is-default: no + ca-type: EXTERNAL + helper-location: $tmpdir/local-submit +CA 'SelfSign': + is-default: no + ca-type: INTERNAL:SELF + next-serial-number: 01 +CA 'IPA': + is-default: no + ca-type: EXTERNAL + helper-location: $libexecdir/ipa-submit +CA 'dogtag-ipa-renew-agent': + is-default: no + ca-type: EXTERNAL + helper-location: $libexecdir/dogtag-ipa-renew-agent-submit + +[[ API ]] +[ simpleprop.py ] +/org/fedorahosted/certmonger/cas/CA5 +/org/fedorahosted/certmonger/cas/CA5 +: -> : -k admin@localhost -> : +0 -> 1 -> 0 +[ walk.py ] +[ /: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted/certmonger: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.add_known_ca ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.add_request ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.find_ca_by_nickname ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.find_request_by_nickname ] +OK + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_known_cas ] +dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA1'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA2'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA3'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA4')], signature=dbus.Signature('o')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_requests ] +dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/requests/Request2')], signature=dbus.Signature('o')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_types ] +dbus.Array([dbus.String('RSA'), dbus.String('EC'), dbus.String('ML-DSA-44'), dbus.String('ML-DSA-65'), dbus.String('ML-DSA-87')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_storage ] +dbus.Array([dbus.String('NSSDB'), dbus.String('FILE')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_cert_storage ] +dbus.Array([dbus.String('NSSDB'), dbus.String('FILE')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.remove_known_ca ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.remove_request ] +OK + +[ /org/fedorahosted/certmonger/requests: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_nickname ] +Buddy + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_autorenew ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_data ] + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_info ] +(dbus.String('CN=$UUID,CN=Local Signing Authority'), dbus.String('$UUID'), dbus.String('CN=localhost'), dbus.Int64(tomorrow), dbus.Array([], signature=dbus.Signature('s')), dbus.Array([dbus.String('localhost')], signature=dbus.Signature('s')), dbus.Array([dbus.String('host/localhost@LOCALHOST')], signature=dbus.Signature('s')), dbus.Int64(9), dbus.Array([dbus.String('1.3.6.1.5.5.7.3.1')], signature=dbus.Signature('s')), dbus.Int64(recently)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_last_checked ] +recently + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_storage_info ] +(dbus.String('FILE'), dbus.String('$tmpdir/test.crt')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_csr_data ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_csr_info ] +(dbus.String('CN=localhost'), dbus.Array([], signature=dbus.Signature('s')), dbus.Array([dbus.String('localhost')], signature=dbus.Signature('s')), dbus.Array([dbus.String('host/localhost@LOCALHOST')], signature=dbus.Signature('s')), dbus.Int64(9), dbus.Array([dbus.String('id-kp-serverAuth')], signature=dbus.Signature('s'))) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_pin ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_pin_file ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_storage_info ] +(dbus.String('FILE'), dbus.String('$tmpdir/test.key')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_type_and_size ] +(dbus.String('RSA'), dbus.Int64(512)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_monitoring ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_notification_info ] +(dbus.String('stdout'), dbus.String('daemon.notice')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_status ] +(dbus.String('MONITORING'), dbus.Boolean(False)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_ca ] +/org/fedorahosted/certmonger/cas/CA1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_submitted_cookie ] +None + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_ca_error ] +None + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_submitted_date ] +recently + +[ /org/fedorahosted/certmonger/requests/Request2 : org.fedorahosted.certmonger.request.modify ] +1 on /org/fedorahosted/certmonger/requests/Request2 +After setting template-eku to 1.2.3.4.5.6.7.8.9.10, we got dbus.Array([dbus.String('1.2.3.4.5.6.7.8.9.10')], signature=dbus.Signature('s'), variant_level=1) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.rekey ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.resubmit ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.refresh ] +0 + +[ /org/fedorahosted/certmonger/cas: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA1: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/local + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_nickname ] +local + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_location ] +$tmpdir/local-submit + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.refresh ] +1 + +[ /org/fedorahosted/certmonger/cas/CA2: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20260112190743 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_nickname ] +SelfSign + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_type ] +INTERNAL:SELF + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_serial ] +01 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_location ] + + +/org/fedorahosted/certmonger/cas/CA2: warning: property org.fedorahosted.certmonger.ca.external-helper not settable on this object +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.refresh ] +1 + +/org/fedorahosted/certmonger/cas/CA2: property org.fedorahosted.certmonger.ca.scep-cipher not set: (, x) +[ /org/fedorahosted/certmonger/cas/CA3: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20260112190743-1 + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_nickname ] +IPA + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_location ] +$libexecdir/ipa-submit + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.refresh ] +1 + +[ /org/fedorahosted/certmonger/cas/CA4: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20260112190743-2 + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_nickname ] +dogtag-ipa-renew-agent + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_location ] +$libexecdir/dogtag-ipa-renew-agent-submit + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.refresh ] +1 + diff --git a/tests/028-dbus/expected.out.nomldsa b/tests/028-dbus/expected.out.nomldsa new file mode 100644 index 0000000..86cba02 --- /dev/null +++ b/tests/028-dbus/expected.out.nomldsa @@ -0,0 +1,965 @@ +Certificate in file "${tmpdir}/test.crt" issued by CA and saved. +Certificate in file "${tmpdir}/test.crt" issued by CA and saved. +[[ getcert ]] +State MONITORING, stuck: no. +Number of certificates and requests being tracked: 1. +Request ID 'Buddy': + status: MONITORING + stuck: no + key pair storage: type=FILE,location='$tmpdir/test.key' + certificate: type=FILE,location='$tmpdir/test.crt' + CA: local + issuer: CN=$UUID,CN=Local Signing Authority + subject: CN=localhost + issued: sometime + expires: sometime + dns: localhost + principal name: host/localhost@LOCALHOST + key usage: digitalSignature,dataEncipherment + eku: id-kp-serverAuth + certificate template/profile: SomeProfileName + profile: SomeProfileName + pre-save command: echo Pre + post-save command: echo Post + track: yes + auto-renew: yes +CA 'local': + is-default: no + ca-type: EXTERNAL + helper-location: $tmpdir/local-submit +CA 'SelfSign': + is-default: no + ca-type: INTERNAL:SELF + next-serial-number: 01 +CA 'IPA': + is-default: no + ca-type: EXTERNAL + helper-location: $libexecdir/ipa-submit +CA 'dogtag-ipa-renew-agent': + is-default: no + ca-type: EXTERNAL + helper-location: $libexecdir/dogtag-ipa-renew-agent-submit + +[[ API ]] +[ simpleprop.py ] +/org/fedorahosted/certmonger/cas/CA5 +/org/fedorahosted/certmonger/cas/CA5 +: -> : -k admin@localhost -> : +0 -> 1 -> 0 +[ walk.py ] +[ /: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted/certmonger: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.add_known_ca ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.add_request ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.find_ca_by_nickname ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.find_request_by_nickname ] +OK + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_known_cas ] +dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA1'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA2'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA3'), dbus.ObjectPath('/org/fedorahosted/certmonger/cas/CA4')], signature=dbus.Signature('o')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_requests ] +dbus.Array([dbus.ObjectPath('/org/fedorahosted/certmonger/requests/Request2')], signature=dbus.Signature('o')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_types ] +dbus.Array([dbus.String('RSA'), dbus.String('DSA'), dbus.String('EC')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_key_storage ] +dbus.Array([dbus.String('NSSDB'), dbus.String('FILE')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger: org.fedorahosted.certmonger.get_supported_cert_storage ] +dbus.Array([dbus.String('NSSDB'), dbus.String('FILE')], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.remove_known_ca ] +OK + +[ /org/fedorahosted/certmonger : org.fedorahosted.certmonger.remove_request ] +OK + +[ /org/fedorahosted/certmonger/requests: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_nickname ] +Buddy + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_autorenew ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_data ] + + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_info ] +(dbus.String('CN=$UUID,CN=Local Signing Authority'), dbus.String('$UUID'), dbus.String('CN=localhost'), dbus.Int64(tomorrow), dbus.Array([], signature=dbus.Signature('s')), dbus.Array([dbus.String('localhost')], signature=dbus.Signature('s')), dbus.Array([dbus.String('host/localhost@LOCALHOST')], signature=dbus.Signature('s')), dbus.Int64(9), dbus.Array([dbus.String('1.3.6.1.5.5.7.3.1')], signature=dbus.Signature('s')), dbus.Int64(recently)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_last_checked ] +recently + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_cert_storage_info ] +(dbus.String('FILE'), dbus.String('$tmpdir/test.crt')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_csr_data ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_csr_info ] +(dbus.String('CN=localhost'), dbus.Array([], signature=dbus.Signature('s')), dbus.Array([dbus.String('localhost')], signature=dbus.Signature('s')), dbus.Array([dbus.String('host/localhost@LOCALHOST')], signature=dbus.Signature('s')), dbus.Int64(9), dbus.Array([dbus.String('id-kp-serverAuth')], signature=dbus.Signature('s'))) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_pin ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_pin_file ] + + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_storage_info ] +(dbus.String('FILE'), dbus.String('$tmpdir/test.key')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_key_type_and_size ] +(dbus.String('RSA'), dbus.Int64(512)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_monitoring ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_notification_info ] +(dbus.String('stdout'), dbus.String('daemon.notice')) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_status ] +(dbus.String('MONITORING'), dbus.Boolean(False)) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_ca ] +/org/fedorahosted/certmonger/cas/CA1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_submitted_cookie ] +None + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_ca_error ] +None + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.get_submitted_date ] +recently + +[ /org/fedorahosted/certmonger/requests/Request2 : org.fedorahosted.certmonger.request.modify ] +1 on /org/fedorahosted/certmonger/requests/Request2 +After setting template-eku to 1.2.3.4.5.6.7.8.9.10, we got dbus.Array([dbus.String('1.2.3.4.5.6.7.8.9.10')], signature=dbus.Signature('s'), variant_level=1) + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.rekey ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.resubmit ] +1 + +[ /org/fedorahosted/certmonger/requests/Request2: org.fedorahosted.certmonger.request.refresh ] +0 + +[ /org/fedorahosted/certmonger/cas: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA1: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/local + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_nickname ] +local + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_location ] +$tmpdir/local-submit + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA1: org.fedorahosted.certmonger.ca.refresh ] +1 + +[ /org/fedorahosted/certmonger/cas/CA2: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20180327134236 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_nickname ] +SelfSign + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_type ] +INTERNAL:SELF + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_serial ] +01 + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_location ] + + +/org/fedorahosted/certmonger/cas/CA2: warning: property org.fedorahosted.certmonger.ca.external-helper not settable on this object +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA2: org.fedorahosted.certmonger.ca.refresh ] +1 + +/org/fedorahosted/certmonger/cas/CA2: property org.fedorahosted.certmonger.ca.scep-cipher not set: (, x) +[ /org/fedorahosted/certmonger/cas/CA3: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20180327134236-1 + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_nickname ] +IPA + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_location ] +$libexecdir/ipa-submit + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA3: org.fedorahosted.certmonger.ca.refresh ] +1 + +[ /org/fedorahosted/certmonger/cas/CA4: org.freedesktop.DBus.Introspectable.Introspect ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_config_file_path ] +$tmpdir/cas/20180327134236-3 + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_nickname ] +dogtag-ipa-renew-agent + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_is_default ] +0 + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_type ] +EXTERNAL + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_serial ] +None + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_location ] +$libexecdir/dogtag-ipa-renew-agent-submit + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.get_issuer_names ] +dbus.Array([], signature=dbus.Signature('s')) + +[ /org/fedorahosted/certmonger/cas/CA4: org.fedorahosted.certmonger.ca.refresh ] +1 + diff --git a/tests/Makefile.am b/tests/Makefile.am index 9977aed..67c9366 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,6 +6,8 @@ CLEANFILES = \ 001-keyiread-ec/actual.err \ 001-keyiread-rsa/actual.out \ 001-keyiread-rsa/actual.err \ + 001-keyiread-mldsa/actual.out \ + 001-keyiread-mldsa/actual.err \ 002-keygen/actual.out \ 002-keygen/actual.err \ 002-keygen-dbm/actual.out \ @@ -18,12 +20,16 @@ CLEANFILES = \ 002-keygen-ec/actual.err \ 002-keygen-rsa/actual.out \ 002-keygen-rsa/actual.err \ + 002-keygen-mldsa/actual.err \ + 002-keygen-mldsa/actual.out \ 003-csrgen/actual.out \ 003-csrgen/actual.err \ 003-csrgen-ec/actual.out \ 003-csrgen-ec/actual.err \ 003-csrgen-rsa/actual.out \ 003-csrgen-rsa/actual.err \ + 003-csrgen-mldsa/actual.out \ + 003-csrgen-mldsa/actual.err \ 004-selfsign/actual.out \ 004-selfsign/actual.err \ 004-selfsign-ec/actual.out \ @@ -145,6 +151,8 @@ EXTRA_DIST = \ 001-keyiread-ec/expected.out.2 \ 001-keyiread-ec/expected.out.3 \ 001-keyiread-ec/expected.out.4 \ + 001-keyiread-mldsa/run.sh \ + 001-keyiread-mldsa/expected.out \ 002-keygen/prequal.sh \ 002-keygen/run.sh \ 002-keygen/expected.out \ @@ -164,12 +172,17 @@ EXTRA_DIST = \ 002-keygen-ec/expected.out.2 \ 002-keygen-ec/expected.out.3 \ 002-keygen-ec/expected.out.4 \ + 002-keygen-mldsa/prequal.sh \ + 002-keygen-mldsa/run.som \ + 002-keygen-mldsa/expected.out \ 003-csrgen/run.sh \ 003-csrgen/expected.out \ 003-csrgen-rsa/run.sh \ 003-csrgen-rsa/expected.out \ 003-csrgen-ec/run.sh \ 003-csrgen-ec/expected.out \ + 003-csrgen-mldsa/run.sh \ + 003-csrgen-mldsa/expected.out \ 004-selfsign/run.sh \ 004-selfsign/expected.out \ 004-selfsign-rsa/run.sh \ @@ -295,6 +308,7 @@ EXTRA_DIST = \ 027-hooks/expected.out \ 027-hooks/run.sh \ 028-dbus/expected.out 028-dbus/expected.out.nodsa \ + 028-dbus/expected.out.nomldsa \ 028-dbus/entry 028-dbus/bogus-entry \ 028-dbus/prequal.sh \ 028-dbus/run.sh \ @@ -450,6 +464,13 @@ subdirs += \ 004-selfsign-ec endif +if HAVE_ML_DSA +subdirs += \ + 001-keyiread-mldsa \ + 002-keygen-mldsa \ + 003-csrgen-mldsa +endif + check: all if [ ! -e $$HOME/.rnd ] ; then \ openssl rand -writerand $$HOME/.rnd; \ From c302313c336fbe700fa2643349da1c450bd7b850 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 7/12] Add abstraction to ML-DSA key sizing to be in line with other algorithms I was discouraged from treating the numeric portion of ML-DSA algorithms as a "strength" per-se but it seems like other crypto products are doing it so I'm adding it for internal consistentcy. --- diff --git a/src/getcert.c b/src/getcert.c index a835328..ea10118 100644 --- a/src/getcert.c +++ b/src/getcert.c @@ -896,6 +896,8 @@ request(const char *argv0, int argc, const char **argv) && (strcasecmp(poptarg, "EC") != 0) #endif #ifdef CM_ENABLE_ML_DSA + && (strcasecmp(poptarg, "MLDSA") != 0) + && (strcasecmp(poptarg, "ML-DSA") != 0) && (strcasecmp(poptarg, "ML-DSA-44") != 0) && (strcasecmp(poptarg, "ML-DSA-65") != 0) && (strcasecmp(poptarg, "ML-DSA-87") != 0) @@ -912,7 +914,7 @@ request(const char *argv0, int argc, const char **argv) printf(" EC"); #endif #ifdef CM_ENABLE_ML_DSA - printf(" ML-DSA-44 ML-DA-65 ML-DSA-87"); + printf(" MLDSA ML-DSA ML-DSA-44 ML-DSA-65 ML-DSA-87"); #endif printf("\n"); return 1; @@ -1141,6 +1143,26 @@ request(const char *argv0, int argc, const char **argv) } } #endif +#ifdef CM_ENABLE_ML_DSA + if ((keytype != NULL) && ((strcasecmp(keytype, "ML-DSA") == 0) || + (strcasecmp(keytype, "MLDSA") == 0))) { + talloc_free(keytype); + switch(keysize) { + case 44: + keytype = talloc_strdup(globals.tctx, "ML-DSA-44"); + break; + case 65: + keytype = talloc_strdup(globals.tctx, "ML-DSA-65"); + break; + case 87: + keytype = talloc_strdup(globals.tctx, "ML-DSA-87"); + break; + default: + keytype = talloc_strdup(globals.tctx, "ML-DSA-65"); + break; + } + } +#endif if ((dbdir != NULL) && (nickname != NULL)) { param[i].key = "KEY_STORAGE"; param[i].value_type = cm_tdbusm_dict_s; @@ -2711,7 +2733,11 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, && (strcasecmp(poptarg, "EC") != 0) #endif #ifdef CM_ENABLE_ML_DSA + && (strcasecmp(poptarg, "MLDSA") != 0) + && (strcasecmp(poptarg, "ML-DSA") != 0) + && (strcasecmp(poptarg, "ML-DSA-44") != 0) && (strcasecmp(poptarg, "ML-DSA-65") != 0) + && (strcasecmp(poptarg, "ML-DSA-87") != 0) #endif ) { printf(_("No support for generating \"%s\" keys.\n"), diff --git a/src/keygen-n.c b/src/keygen-n.c index 14b7c7d..a43dbd4 100644 --- a/src/keygen-n.c +++ b/src/keygen-n.c @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -274,15 +275,17 @@ cm_keygen_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pmech = CKM_EC_KEY_PAIR_GEN; break; #endif -#ifdef CM_ENABLE_ML_DSA: +#ifdef CM_ENABLE_ML_DSA case cm_key_ml_dsa_44: cm_requested_key_size = ML_DSA_44_PRIVATEKEY_LEN; mech = CKM_ML_DSA_KEY_PAIR_GEN; pmech = CKM_ML_DSA_KEY_PAIR_GEN; + break; case cm_key_ml_dsa_65: cm_requested_key_size = ML_DSA_65_PRIVATEKEY_LEN; mech = CKM_ML_DSA_KEY_PAIR_GEN; pmech = CKM_ML_DSA_KEY_PAIR_GEN; + break; case cm_key_ml_dsa_87: cm_requested_key_size = ML_DSA_87_PRIVATEKEY_LEN; mech = CKM_ML_DSA_KEY_PAIR_GEN; @@ -550,7 +553,7 @@ retry_gen: /* no parameters to generate */ break; #endif -#ifdef CM_ENABLE_ML_DSA: +#ifdef CM_ENABLE_ML_DSA case cm_key_ml_dsa_44: case cm_key_ml_dsa_65: case cm_key_ml_dsa_87: @@ -592,20 +595,23 @@ retry_gen: params = &ec_params; break; #endif -#ifdef CM_ENABLE_ML_DSA: +#ifdef CM_ENABLE_ML_DSA case cm_key_ml_dsa_44: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_44; + cm_log(0, "params set to 44\n"); params = &ml_params; break; case cm_key_ml_dsa_65: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_65; + cm_log(0, "params set to 65\n"); params = &ml_params; break; case cm_key_ml_dsa_87: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_87; + cm_log(0, "params set to 87\n"); params = &ml_params; break; #endif @@ -614,7 +620,7 @@ retry_gen: break; } /* Generate the key pair. */ - cm_log(1, "Generating key pair of size %d.\n", cm_key_size); + cm_log(0, "Generating key pair of size %d.\n", cm_key_size); pubkey = NULL; privkey = PK11_GenerateKeyPair(slot, mech, params, &pubkey, PR_TRUE, PR_TRUE, NULL); diff --git a/src/keyiread-n.c b/src/keyiread-n.c index 1f08840..bd12970 100644 --- a/src/keyiread-n.c +++ b/src/keyiread-n.c @@ -555,8 +555,8 @@ cm_keyiread_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, alg = "EC"; break; case mldsaKey: - cm_log(3, "Key is an ML-DSA key.\n"); alg = cm_GetSignatureAlgorithmFromPrivateKey(keys->privkey); + cm_log(3, "Key is an ML-DSA key %s.\n", alg); break; case nullKey: default: From 490dd12a132e9abd2579e1d759c56a6e9671e138 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 8/12] Add helper function to extract an ML-DSA public key i2d_PublicKey() for an ML-DSA key will return nothing because the capability is not registered. Pull it out on our own. This is needed to extract the authorityKeyIdentifier Signed-off-by: Rob Crittenden --- diff --git a/src/keygen-o.c b/src/keygen-o.c index 2f5a212..adee833 100644 --- a/src/keygen-o.c +++ b/src/keygen-o.c @@ -438,12 +438,12 @@ retry_gen: } } pubhex = ""; - len = i2d_PublicKey(pkey, NULL); + len = util_i2d_PublicKey(pkey, NULL); if (len > 0) { p = malloc(len); if (p != NULL) { q = p; - if (i2d_PublicKey(pkey, &q) == len) { + if (util_i2d_PublicKey(pkey, &q) == len) { pubhex = cm_store_hex_from_bin(NULL, p, q - p); } free(p); diff --git a/src/keyiread-o.c b/src/keyiread-o.c index d014ca7..fc9c752 100644 --- a/src/keyiread-o.c +++ b/src/keyiread-o.c @@ -32,19 +32,9 @@ #include #include +#include #include -/************************ - * FIXME: these definitions aren't public in openssl? wrong algos? -************************/ -#define NID_ML_DSA_44 1457 -#define NID_ML_DSA_65 1458 -#define NID_ML_DSA_87 1459 - -# define EVP_PKEY_ML_DSA_44 NID_ML_DSA_44 -# define EVP_PKEY_ML_DSA_65 NID_ML_DSA_65 -# define EVP_PKEY_ML_DSA_87 NID_ML_DSA_87 - #include #include "keyiread.h" @@ -163,8 +153,6 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pubkey = ""; pubikey = ""; if (pkey != NULL) { - char *n = EVP_PKEY_get0_type_name(pkey); - switch (util_EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: cm_log(3, "Key is an RSA key.\n"); @@ -182,25 +170,25 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, alg = "EC"; break; #endif + default: #ifdef CM_ENABLE_ML_DSA - case 0: /* no key type base */ char *name = EVP_PKEY_get0_type_name(pkey); - cm_log(3, "Key is a %s key.\n", name); + cm_log(3, "Key is a %s key.\n", + (name != NULL) ? name : "(unknown)"); - if (strcasecmp(name, "ML-DSA-44") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-44")) { alg = "ML-DSA-44"; break; } else - if (strcasecmp(name, "ML-DSA-65") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-65")) { alg = "ML-DSA-65"; break; } else - if (strcasecmp(name, "ML-DSA-87") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-87")) { alg = "ML-DSA-87"; break; } #endif - default: cm_log(3, "Key is for an unknown algorithm.\n"); alg = ""; break; @@ -213,12 +201,19 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pubikey = cm_store_hex_from_bin(NULL, tmp, length); } tmp = NULL; - length = i2d_PublicKey(pkey, NULL); + length = util_i2d_PublicKey(pkey, NULL); if (length > 0) { tmp = malloc(length); if (tmp != NULL) { - length = i2d_PublicKey(pkey, (unsigned char **) &tmp); - pubkey = cm_store_hex_from_bin(NULL, tmp, length); + /* keep a pointer to the start of the buffer otherwise + * after util_i2d_Public_key() is called then pointer is + * at the end of the buffer and store_next_from_bin will + * read beyond its buffer and at best output garbage. + */ + unsigned char *q = tmp; + + length = util_i2d_PublicKey(pkey, &q); + pubkey = cm_store_hex_from_bin(NULL, tmp, q - tmp); } } } else { @@ -243,24 +238,21 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, alg = "EC"; break; #endif + default: #ifdef CM_ENABLE_ML_DSA - case 0: /* no key type base */ - char *name = EVP_PKEY_get0_type_name(pkey); - - if (strcasecmp(name, "ML-DSA-44") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-44")) { alg = "ML-DSA-44"; break; } else - if (strcasecmp(name, "ML-DSA-65") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-65")) { alg = "ML-DSA-65"; break; } else - if (strcasecmp(name, "ML-DSA-87") == 0) { + if (EVP_PKEY_is_a(pkey, "ML-DSA-87")) { alg = "ML-DSA-87"; break; } #endif - default: cm_log(3, "Next key is for an unknown algorithm.\n"); alg = ""; break; @@ -273,12 +265,19 @@ cm_keyiread_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, pubikey = cm_store_hex_from_bin(NULL, tmp, length); } tmp = NULL; - length = i2d_PublicKey(nextpkey, NULL); + length = util_i2d_PublicKey(nextpkey, NULL); if (length > 0) { tmp = malloc(length); if (tmp != NULL) { - length = i2d_PublicKey(nextpkey, (unsigned char **) &tmp); - pubkey = cm_store_hex_from_bin(NULL, tmp, length); + /* keep a pointer to the start of the buffer otherwise + * after util_i2d_Public_key() is called then pointer is + * at the end of the buffer and store_next_from_bin will + * read beyond its buffer and at best output garbage. + */ + unsigned char *q = tmp; + + length = util_i2d_PublicKey(nextpkey, &q); + pubkey = cm_store_hex_from_bin(NULL, tmp, q - tmp); } } fprintf(fp, "%s/%d/%s/%s\n", alg, bits, pubikey, pubkey); diff --git a/src/util-o.c b/src/util-o.c index 7feecb9..1263eb0 100644 --- a/src/util-o.c +++ b/src/util-o.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -590,6 +591,32 @@ util_EVP_PKEY_dup(EVP_PKEY *pkey, EVP_PKEY * util_public_EVP_PKEY_dup(EVP_PKEY *pkey) { +#ifdef CM_ENABLE_ML_DSA + if (EVP_PKEY_is_a(pkey, "ML-DSA-44") || + EVP_PKEY_is_a(pkey, "ML-DSA-65") || + EVP_PKEY_is_a(pkey, "ML-DSA-87")) { + long len; + unsigned char *buf, *q; + const unsigned char *cp; + EVP_PKEY *out; + + len = i2d_PUBKEY(pkey, NULL); + if (len <= 0) + return NULL; + buf = malloc(len); + if (buf == NULL) + return NULL; + q = buf; + if (i2d_PUBKEY(pkey, &q) != len) { + free(buf); + return NULL; + } + cp = buf; + out = d2i_PUBKEY(NULL, &cp, len); + free(buf); + return out; + } +#endif return util_EVP_PKEY_dup(pkey, i2d_PublicKey, d2i_PublicKey); } @@ -742,3 +769,100 @@ done: return ret; } + +/* + * Return a raw ML-DSA public key to match the type of value returned by + * i2d_PublicKey() for classic algorithms. The OpenSSL i2d_PublicKey() + * does not support ML-DSA. It needs to be extracted in a different way. + * + * Returns encoded length, or -1 on error. + */ +#ifdef CM_ENABLE_ML_DSA +int +i2d_MLDSA_PublicKey(const EVP_PKEY *pkey, unsigned char **pp) +{ + const char *const types[3] = { "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" }; + const int pk_lens[3] = { 1312, 1952, 2592 }; + int i, pk_len; + size_t pub_len; + + if (pkey == NULL) + return -1; + + for (i = 0; i < 3; i++) { + if (EVP_PKEY_is_a(pkey, types[i])) + break; + } + if (i >= 3) { + cm_log(1, "i2d_MLDSA_PublicKey: This is not an ML-DSA key\n"); + return -1; + } + pk_len = pk_lens[i]; + + if (pp == NULL) { + pub_len = 0; + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, + NULL, 0, &pub_len)) + return -1; + if (pub_len != (size_t)pk_len) { + cm_log(1, "i2d_MLDSA_PublicKey: The public key length is %ld, expected %d.\n", + pub_len, pk_len); + return -1; + } + return pk_len; + } + + if (*pp == NULL) { + *pp = malloc((size_t)pk_len); + if (*pp == NULL) { + cm_log(1, "i2d_MLDSA_PublicKey: malloc() failed.\n"); + return -1; + } + pub_len = (size_t)pk_len; + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, + *pp, (size_t)pk_len, + &pub_len)) { + free(*pp); + *pp = NULL; + cm_log(1, "i2d_MLDSA_PublicKey: EVP_PKEY_get_octet_string_param() failed.\n"); + return -1; + } + if (pub_len != (size_t)pk_len) { + cm_log(1, "i2d_MLDSA_PublicKey: EVP_PKEY_get_octet_string_param() failed after malloc.\n"); + free(*pp); + *pp = NULL; + return -1; + } + return pk_len; + } + + /* The buffer is pre-allocated */ + pub_len = (size_t)pk_len; + if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, + *pp, (size_t)pk_len, &pub_len)) { + cm_log(1, "i2d_MLDSA_PublicKey: EVP_PKEY_get_octet_string_param() failed.\n"); + return -1; + } + if (pub_len != (size_t)pk_len) { + cm_log(1, "i2d_MLDSA_PublicKey: The public key length is %ld, expected %d.\n", + pub_len, pk_len); + return -1; + } + *pp += pk_len; + return pk_len; +} +#endif + +int +util_i2d_PublicKey(const EVP_PKEY *pkey, unsigned char **pp) +{ + if (pkey == NULL) + return -1; +#ifdef CM_ENABLE_ML_DSA + if (EVP_PKEY_is_a(pkey, "ML-DSA-44") || + EVP_PKEY_is_a(pkey, "ML-DSA-65") || + EVP_PKEY_is_a(pkey, "ML-DSA-87")) + return i2d_MLDSA_PublicKey(pkey, pp); +#endif + return i2d_PublicKey(pkey, pp); +} diff --git a/src/util-o.h b/src/util-o.h index 8550e07..ca24917 100644 --- a/src/util-o.h +++ b/src/util-o.h @@ -78,5 +78,6 @@ void util_NETSCAPE_SPKI_set_sig_alg(NETSCAPE_SPKI *spki, const X509_ALGOR *sig_a EVP_PKEY *util_public_EVP_PKEY_dup(EVP_PKEY *pkey); EVP_PKEY *util_private_EVP_PKEY_dup(EVP_PKEY *pkey); int validate_pem(void *parent, const char *path); +int util_i2d_PublicKey(const EVP_PKEY *pkey, unsigned char **pp); #endif diff --git a/tests/001-keyiread-mldsa/expected.out b/tests/001-keyiread-mldsa/expected.out index 82ab39a..7003fde 100644 --- a/tests/001-keyiread-mldsa/expected.out +++ b/tests/001-keyiread-mldsa/expected.out @@ -1,7 +1,13 @@ OK (ML-DSA-44:10496). OK (ML-DSA-44:10496). +crosscheck ML-DSA-44 key_pubkey: NSS == OpenSSL (FILE) +crosscheck ML-DSA-44 key_pubkey_info: NSS == OpenSSL (FILE) OK (ML-DSA-65:15616). OK (ML-DSA-65:15616). +crosscheck ML-DSA-65 key_pubkey: NSS == OpenSSL (FILE) +crosscheck ML-DSA-65 key_pubkey_info: NSS == OpenSSL (FILE) OK (ML-DSA-87:20736). OK (ML-DSA-87:20736). +crosscheck ML-DSA-87 key_pubkey: NSS == OpenSSL (FILE) +crosscheck ML-DSA-87 key_pubkey_info: NSS == OpenSSL (FILE) Test complete. diff --git a/tests/001-keyiread-mldsa/run.sh b/tests/001-keyiread-mldsa/run.sh index 9c01c2f..0fb1ed5 100755 --- a/tests/001-keyiread-mldsa/run.sh +++ b/tests/001-keyiread-mldsa/run.sh @@ -1,23 +1,73 @@ #!/bin/bash -e +# +# ML-DSA key material: create keys in NSS, read with keyiread (NSSDB entry), +# export PKCS#12 to PEM, read with keyiread (FILE entry). Then assert +# key_pubkey (raw) and key_pubkey_info (SPKI hex) saved on both entries match. +# cd "$tmpdir" source "$srcdir"/functions initnssdb "$tmpdir" +# Extract a multi-line store-file field: "field=value" then optional +# continuation lines " rest..." (leading space, see store-files.c). +get_store_field() { + local fld="$1" file="$2" + awk -v fld="$fld" ' + BEGIN { found = 0 } + $0 ~ "^" fld "=" { + found = 1 + v = substr($0, length(fld) + 2) + while (getline > 0) { + if ($0 ~ /^ /) + v = v substr($0, 2) + else { + print v + exit 0 + } + } + print v + exit 0 + } + END { + if (!found) + exit 1 + } + ' "$file" +} + +crosscheck_pubfields() { + local size="$1" nss_entry="$2" ossl_entry="$3" f n o + + for f in key_pubkey key_pubkey_info; do + n=$(get_store_field "$f" "$nss_entry") || { + echo "missing $f in $nss_entry" >&2 + return 1 + } + o=$(get_store_field "$f" "$ossl_entry") || { + echo "missing $f in $ossl_entry" >&2 + return 1 + } + if test "$n" != "$o"; then + echo "crosscheck $size: $f differs between NSS and OpenSSL entry" >&2 + return 1 + fi + echo "crosscheck $size $f: NSS == OpenSSL (FILE)" + done +} + for size in ML-DSA-44 ML-DSA-65 ML-DSA-87 ; do - # Generate a self-signed cert. + # Generate a self-signed cert (key in NSS). run_certutil -d "$tmpdir" -S -n keyi$size \ -s "cn=T$size" -c "cn=T$size" \ -x -t u -k mldsa -q $size - # Check the size of the key. cat > entry.nss.$size <<- EOF key_storage_type=NSSDB key_storage_location=$tmpdir key_nickname=keyi$size EOF $toolsdir/keyiread entry.nss.$size - # Export the key. if ! pk12util -C AES-128-CBC -c AES-128-CBC -d "$tmpdir" -o $size.p12 -W "" -n "keyi$size" > /dev/null 2>&1 ; then echo Error exporting key for $size, continuing. continue @@ -32,5 +82,6 @@ for size in ML-DSA-44 ML-DSA-65 ML-DSA-87 ; do key_nickname=keyi$size EOF $toolsdir/keyiread entry.openssl.$size + crosscheck_pubfields "$size" "entry.nss.$size" "entry.openssl.$size" done echo Test complete. From c805934b8a229d749386d459de36296a10a6a27b Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 9/12] csrgen-o.c: implement a new way to get the signing algorithm cm_key_algorithm may be unspecified in entry for a newly requested key, even if the key has been generated. Use the entry key type to determine the signing algorithm if present, otherwise use the private key to determine the algorithm. This way we can be more sure that the correct value is being passed to X509_REQ_sign. Signed-off-by: Rob Crittenden --- diff --git a/src/csrgen-o.c b/src/csrgen-o.c index e1f8c4a..2c130f4 100644 --- a/src/csrgen-o.c +++ b/src/csrgen-o.c @@ -80,6 +80,56 @@ astring_type(const char *attr, const char *p, ssize_t n) return V_ASN1_PRINTABLESTRING; } +/* cm_key_algorithm may be unspecified for a newly requested key, even if + * the key has been generated. + * + * Use the entry key type to determine the signing algorithm if present, + * otherwise use the private key to determine the algorithm. + * + */ +static enum cm_key_algorithm +get_signing_algorithm(const struct cm_store_entry *entry, const EVP_PKEY *pkey) +{ + enum cm_key_algorithm alg; + + if (pkey == NULL) { + alg = entry->cm_key_type.cm_key_algorithm; + if (alg == cm_key_unspecified) { + alg = entry->cm_key_type.cm_key_gen_algorithm; + } + return alg; + } +#ifdef CM_ENABLE_ML_DSA + if (EVP_PKEY_is_a(pkey, "ML-DSA-44")) { + return cm_key_ml_dsa_44; + } + if (EVP_PKEY_is_a(pkey, "ML-DSA-65")) { + return cm_key_ml_dsa_65; + } + if (EVP_PKEY_is_a(pkey, "ML-DSA-87")) { + return cm_key_ml_dsa_87; + } +#endif + if (EVP_PKEY_is_a(pkey, "RSA")) { + return cm_key_rsa; + } +#ifdef CM_ENABLE_DSA + if (EVP_PKEY_is_a(pkey, "DSA")) { + return cm_key_dsa; + } +#endif +#ifdef CM_ENABLE_EC + if (EVP_PKEY_is_a(pkey, "EC")) { + return cm_key_ecdsa; + } +#endif + alg = entry->cm_key_type.cm_key_algorithm; + if (alg == cm_key_unspecified) { + alg = entry->cm_key_type.cm_key_gen_algorithm; + } + return alg; +} + static X509_NAME * ldap_dn_to_X509_NAME(char *s) { LDAPDN dn = NULL; @@ -191,7 +241,7 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, unsigned int bmpcount, mdlen; long error; int i; - const EVP_MD *sign_md = cm_prefs_ossl_sig_alg(entry->cm_key_type.cm_key_algorithm); + const EVP_MD *sign_md; status = fdopen(fd, "w"); if (status == NULL) { @@ -263,6 +313,9 @@ cm_csrgen_o_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, } } if (pkey != NULL) { + enum cm_key_algorithm sign_alg = get_signing_algorithm(entry, pkey); + sign_md = cm_prefs_ossl_sig_alg(sign_alg); + req = X509_REQ_new(); if (req != NULL) { subject = NULL; diff --git a/src/keygen-n.c b/src/keygen-n.c index a43dbd4..2743a9a 100644 --- a/src/keygen-n.c +++ b/src/keygen-n.c @@ -599,19 +599,16 @@ retry_gen: case cm_key_ml_dsa_44: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_44; - cm_log(0, "params set to 44\n"); params = &ml_params; break; case cm_key_ml_dsa_65: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_65; - cm_log(0, "params set to 65\n"); params = &ml_params; break; case cm_key_ml_dsa_87: memset(&ml_params, 0, sizeof(ml_params)); ml_params = CKP_ML_DSA_87; - cm_log(0, "params set to 87\n"); params = &ml_params; break; #endif diff --git a/src/keygen-o.c b/src/keygen-o.c index adee833..fc4469c 100644 --- a/src/keygen-o.c +++ b/src/keygen-o.c @@ -320,9 +320,6 @@ retry_gen: break; } - char *n = EVP_PKEY_get0_type_name(pkey); - cm_log(1, "debug: generated type %s for %d\n", n, cm_key_algorithm); - filename = strdup(entry->cm_key_storage_location); marker = ""; keyfd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); From 70956f089650852fdc0bb25c3d70dc6cd142ea5a Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 10/12] Add a function to return the right digest for signing This returns the signature digest, or ML-DSA pseudo digest, to be used in X509_sign/X509_REQ_sign calls. It is valid in OpenSSL to pass in NULL for pure ML-DSA when signing. Signed-off-by: Rob Crittenden --- diff --git a/src/submit-o.c b/src/submit-o.c index 5bd23af..019ae12 100644 --- a/src/submit-o.c +++ b/src/submit-o.c @@ -58,6 +58,33 @@ #include "subproc.h" #include "util-o.h" +/* + * Return the digest (or ML-DSA signature pseudo-digest to be used + * in X509_sign/X509_REQ_sign calls using this private key (pkey). + * + * Returns NULL if ML-DSA digest names are not registered. It is + * valid in OpenSSL to pass in NULL for pure ML-DSA. + */ +const EVP_MD * +get_digest_for_pkey(const EVP_PKEY *pkey) +{ + if (pkey == NULL) { + return cm_prefs_ossl_hash(); + } +#ifdef CM_ENABLE_ML_DSA + if (EVP_PKEY_is_a(pkey, "ML-DSA-44")) { + return EVP_get_digestbyname("ML-DSA-44"); + } + if (EVP_PKEY_is_a(pkey, "ML-DSA-65")) { + return EVP_get_digestbyname("ML-DSA-65"); + } + if (EVP_PKEY_is_a(pkey, "ML-DSA-87")) { + return EVP_get_digestbyname("ML-DSA-87"); + } +#endif + return cm_prefs_ossl_hash(); +} + static void cm_submit_o_set_things(X509 **cert, X509 *signer, unsigned char uuid[16], unsigned int uuid_len, STACK_OF(X509_EXTENSION) *extensions) @@ -258,7 +285,7 @@ cm_submit_o_sign(void *parent, char *csr, } #endif /* Add a signature so that it looks right...ish. */ - X509_sign(*cert, signer_key, cm_prefs_ossl_hash()); + X509_sign(*cert, signer_key, get_digest_for_pkey(signer_key)); /* Add extensions and possibly add deprecated UUIDs. */ cm_submit_o_set_things(cert, signer, uuid, uuid_len, X509_REQ_get_extensions(req)); @@ -291,7 +318,7 @@ cm_submit_o_sign(void *parent, char *csr, } /* finish up */ if (signer_key != NULL) { - X509_sign(*cert, signer_key, cm_prefs_ossl_hash()); + X509_sign(*cert, signer_key, get_digest_for_pkey(signer_key)); status = CM_SUBMIT_STATUS_ISSUED; } else { status = CM_SUBMIT_STATUS_UNREACHABLE; From 0314c2fe5be712519c91b0bf42d1074c55c7f5d0 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Apr 30 2026 19:58:47 +0000 Subject: [PATCH 11/12] Add a selfsign test for ML-DSA Based on 004-selfsign-rsa this will help prevent regressions in signing certificates. Because ML-DSA always incorporates some randomness in its signing we can't do a straight comparison of the resulting certificates. Instead we make sure they look more or less identical with the same subject, dates and pub key. Signed-off-by: Rob Crittenden --- diff --git a/src/submit-o.c b/src/submit-o.c index 019ae12..9bc6fc6 100644 --- a/src/submit-o.c +++ b/src/submit-o.c @@ -59,11 +59,12 @@ #include "util-o.h" /* - * Return the digest (or ML-DSA signature pseudo-digest to be used - * in X509_sign/X509_REQ_sign calls using this private key (pkey). * - * Returns NULL if ML-DSA digest names are not registered. It is - * valid in OpenSSL to pass in NULL for pure ML-DSA. + * This returns the signature digest, or ML-DSA pseudo digest, + * to be used in X509_sign/X509_REQ_sign calls. + * + * It is valid in OpenSSL to pass in NULL for pure ML-DSA when + * signing. */ const EVP_MD * get_digest_for_pkey(const EVP_PKEY *pkey) diff --git a/tests/004-selfsign-mldsa/expected.out b/tests/004-selfsign-mldsa/expected.out new file mode 100644 index 0000000..f0c6d13 --- /dev/null +++ b/tests/004-selfsign-mldsa/expected.out @@ -0,0 +1,4 @@ +ML-DSA-44 OK. +ML-DSA-65 OK. +ML-DSA-87 OK. +Test complete. diff --git a/tests/004-selfsign-mldsa/run.sh b/tests/004-selfsign-mldsa/run.sh new file mode 100755 index 0000000..8d76204 --- /dev/null +++ b/tests/004-selfsign-mldsa/run.sh @@ -0,0 +1,109 @@ +#!/bin/bash -e + +#srcdir="/home/vagrant/rpmbuild/SOURCES/certmonger-0.79.21/tests" +#toolsdir="/home/vagrant/rpmbuild/SOURCES/certmonger-0.79.21/tests/tools" +#tmpdir="/tmp/test" + +cd "$tmpdir" + +source "$srcdir"/functions +initnssdb "$tmpdir" + +grep -v ^validity_period $CERTMONGER_CONFIG_DIR/certmonger.conf > \ + $tmpdir/certmonger.conf +cat >> $tmpdir/certmonger.conf << EOF +[selfsign] +validity_period = 46129s +EOF + +function append() { + cat >> $1 <<- EOF + template_subject=CN=Babs Jensen + template_hostname=localhost,localhost.localdomain + template_email=root@localhost,root@localhost.localdomain + template_principal=root@EXAMPLE.COM,root@FOO.EXAMPLE.COM + template_ku=111 + template_eku=id-kp-clientAuth,id-kp-emailProtection + EOF +} + +function setupca() { + cat > ca.self <<- EOF + id=self_signer + ca_is_default=0 + ca_type=INTERNAL:SELF + ca_internal_serial=04 + ca_internal_issue_time=40271 + EOF +} + +# ML-DSA signatures include fresh randomness, so full DER/PEM of two certs +# produced from the same key and template will almost always differ. Compare +# stable fields and verify each cert instead of cmp on the whole file. +function compare_mldsa_certs() { + local nss=$1 ossl=$2 + local d n o + n="n" + o="o" + openssl x509 -in "$nss" -noout -serial > "$n.s" && openssl x509 -in "$ossl" -noout -serial > "$o.s" + cmp "$n.s" "$o.s" + openssl x509 -in "$nss" -noout -subject > "$n.sj" && openssl x509 -in "$ossl" -noout -subject > "$o.sj" + cmp "$n.sj" "$o.sj" + openssl x509 -in "$nss" -noout -issuer > "$n.is" && openssl x509 -in "$ossl" -noout -issuer > "$o.is" + cmp "$n.is" "$o.is" + openssl x509 -in "$nss" -noout -dates > "$n.dt" && openssl x509 -in "$ossl" -noout -dates > "$o.dt" + cmp "$n.dt" "$o.dt" + openssl x509 -in "$nss" -noout -pubkey > "$n.pk" && openssl x509 -in "$ossl" -noout -pubkey > "$o.pk" + cmp "$n.pk" "$o.pk" + openssl x509 -in "$nss" -noout -ext subjectKeyIdentifier > "$n.skid" && openssl x509 -in "$ossl" -noout -ext subjectKeyIdentifier > "$o.skid" + cmp "$n.skid" "$o.skid" +# openssl verify -CAfile "$nss" "$nss" > /dev/null +# openssl verify -CAfile "$ossl" "$ossl" > /dev/null +} + +for size in ML-DSA-44 ML-DSA-65 ML-DSA-87; do + # Build a self-signed certificate. + run_certutil -d "$tmpdir" -S -g $size -n keyi$size \ + -s "cn=T$size" -c "cn=T$size" \ + -x -t u -k mldsa -q $size + # Export the certificate and key. + pk12util -C AES-128-CBC -c AES-128-CBC -d "$tmpdir" -o $size.p12 -W "" -n "keyi$size" > /dev/null 2>&1 + openssl pkcs12 -in $size.p12 -passin pass: -out key.$size -nodes > /dev/null 2>&1 + # Read that OpenSSL key. + cat > entry.$size <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/key.$size + EOF + $toolsdir/keyiread entry.$size > /dev/null 2>&1 + grep ^key_pubkey_info= entry.$size > pubkey.$size + grep ^key_pubkey= entry.$size >> pubkey.$size + # Use that NSS key. + cat > entry.$size <<- EOF + key_storage_type=NSSDB + key_storage_location=$tmpdir + key_nickname=keyi$size + EOF + append entry.$size + cat pubkey.$size >> entry.$size + $toolsdir/csrgen entry.$size > csr.nss.$size + setupca + $toolsdir/submit ca.self entry.$size > cert.nss.$size + # Use that OpenSSL key. + cat > entry.$size <<- EOF + key_storage_type=FILE + key_storage_location=$tmpdir/key.$size + EOF + append entry.$size + cat pubkey.$size >> entry.$size + $toolsdir/csrgen entry.$size > csr.openssl.$size + setupca + $toolsdir/submit ca.self entry.$size > cert.openssl.$size + # Now compare them. + if ! compare_mldsa_certs cert.nss.$size cert.openssl.$size ; then + echo "Certificates differ (non-signature fields or verify failed) for $size" + exit 1 + else + echo $size OK. + fi +done +echo Test complete. diff --git a/tests/Makefile.am b/tests/Makefile.am index 67c9366..4049bcb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,6 +36,8 @@ CLEANFILES = \ 004-selfsign-ec/actual.err \ 004-selfsign-rsa/actual.out \ 004-selfsign-rsa/actual.err \ + 004-selfsign-mldsa/actual.out \ + 004-selfsign-mldsa/actual.err \ 005-dbusm/actual.out \ 005-dbusm/actual.err \ 006-serial/actual.out \ @@ -173,7 +175,7 @@ EXTRA_DIST = \ 002-keygen-ec/expected.out.3 \ 002-keygen-ec/expected.out.4 \ 002-keygen-mldsa/prequal.sh \ - 002-keygen-mldsa/run.som \ + 002-keygen-mldsa/run.sh \ 002-keygen-mldsa/expected.out \ 003-csrgen/run.sh \ 003-csrgen/expected.out \ @@ -189,6 +191,8 @@ EXTRA_DIST = \ 004-selfsign-rsa/expected.out \ 004-selfsign-ec/run.sh \ 004-selfsign-ec/expected.out \ + 004-selfsign-mldsa/run.sh \ + 004-selfsign-mldsa/expected.out \ 005-dbusm/run.sh \ 005-dbusm/expected.out \ 006-serial/run.sh \ @@ -469,6 +473,7 @@ subdirs += \ 001-keyiread-mldsa \ 002-keygen-mldsa \ 003-csrgen-mldsa + 004-selfsign-mldsa endif check: all From a64c897d2137c66ea4bbdbad0a5e12500b8083c7 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: May 05 2026 14:03:19 +0000 Subject: [PATCH 12/12] Skip the mldsa tests if NSS is not capable of mldsa This stacks on top of the configure tests to see if mldsa is available in both NSS and OpenSSL. If not then that should prevent the tests from being executed altogether. The more likely case is if mldsa is not allowed in NSS crypto policy. I've seen that in the wild. Signed-off-by: Rob Crittenden --- diff --git a/tests/001-keyiread-mldsa/prequal.sh b/tests/001-keyiread-mldsa/prequal.sh new file mode 100755 index 0000000..e1ae5a5 --- /dev/null +++ b/tests/001-keyiread-mldsa/prequal.sh @@ -0,0 +1,12 @@ +#!/bin/sh +source "$srcdir"/functions +initnssdb "$tmpdir" + +run_certutil -d "$tmpdir" -S -n test \ + -s "cn=Test" -c "cn=Test" \ + -x -t u -k mldsa -q ML-DSA-65 + +if [ $? -ne 0 ]; then + echo "ML-DSA is not available or not allowed in NSS crypto policy." + exit 1 +fi diff --git a/tests/002-keygen-mldsa/prequal.sh b/tests/002-keygen-mldsa/prequal.sh old mode 100644 new mode 100755 diff --git a/tests/003-csrgen-mldsa/prequal.sh b/tests/003-csrgen-mldsa/prequal.sh new file mode 100755 index 0000000..e1ae5a5 --- /dev/null +++ b/tests/003-csrgen-mldsa/prequal.sh @@ -0,0 +1,12 @@ +#!/bin/sh +source "$srcdir"/functions +initnssdb "$tmpdir" + +run_certutil -d "$tmpdir" -S -n test \ + -s "cn=Test" -c "cn=Test" \ + -x -t u -k mldsa -q ML-DSA-65 + +if [ $? -ne 0 ]; then + echo "ML-DSA is not available or not allowed in NSS crypto policy." + exit 1 +fi diff --git a/tests/004-selfsign/prequal.sh b/tests/004-selfsign/prequal.sh new file mode 100755 index 0000000..e1ae5a5 --- /dev/null +++ b/tests/004-selfsign/prequal.sh @@ -0,0 +1,12 @@ +#!/bin/sh +source "$srcdir"/functions +initnssdb "$tmpdir" + +run_certutil -d "$tmpdir" -S -n test \ + -s "cn=Test" -c "cn=Test" \ + -x -t u -k mldsa -q ML-DSA-65 + +if [ $? -ne 0 ]; then + echo "ML-DSA is not available or not allowed in NSS crypto policy." + exit 1 +fi