From: Stefan Berger Date: Tue, 14 Oct 2025 22:52:45 +0000 (-0500) Subject: cms: Get a default hash for hash-less signing schemes X-Git-Tag: 4.0-PRE-CLANG-FORMAT-WEBKIT~183 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e24de9f31736fbf60a8a2a9364f91ab0bf9f100b;p=thirdparty%2Fopenssl.git cms: Get a default hash for hash-less signing schemes Get a default hash for hash-less signing schemes such as ML-DSA, SLH-DSA, and EdDSA in the case when signed attributes are present as well as for the no signed attributes case. For the latter case, EdDSA is the only signing scheme that has a required hash (sha512 for ED25519 and shake256 for ED448), all other ones have a suggested hash. Only use the suggested hash if the hash provided by the caller of CMS_add1_signer passed a NULL pointer for md. Use the required hash in any case, overriding any choice of the caller. Fixes: #13523 Signed-off-by: Stefan Berger Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/28923) --- diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c index 80f7d73d6dd..7f7059df289 100644 --- a/crypto/cms/cms_sd.c +++ b/crypto/cms/cms_sd.c @@ -341,6 +341,112 @@ static int ossl_cms_add1_signing_cert_v2(CMS_SignerInfo *si, return ret; } +static const struct { + const char *name; + int md_nid; + int md_a_must; /* md is a 'MUST' */ + int noattr_md_nid; /* in case of 'without signed attributes' */ + int noattr_md_a_must; /* noattr_md is a 'MUST' not a 'SHOULD' */ +} key2data[] = { + { "ML-DSA-44", NID_shake256, 0, NID_sha512, 0, }, + { "ML-DSA-65", NID_shake256, 0, NID_sha512, 0, }, + { "ML-DSA-87", NID_shake256, 0, NID_sha512, 0, }, + { "ED25519", NID_sha512, 0, NID_sha512, 1, }, /* RFC 8419 */ + /* RFC 8419 3.1 ED448: id-shake256-len MUST be used => NID_undef for now */ + { "ED448", NID_undef, 1, NID_shake256, 1, }, + { "SLH-DSA-SHA2-128f", NID_sha256, 0, NID_sha256, 0, }, /* RFC 9814 */ + { "SLH-DSA-SHA2-128s", NID_sha256, 0, NID_sha256, 0, }, + { "SLH-DSA-SHA2-192f", NID_sha512, 0, NID_sha512, 0, }, + { "SLH-DSA-SHA2-192s", NID_sha512, 0, NID_sha512, 0, }, + { "SLH-DSA-SHA2-256f", NID_sha512, 0, NID_sha512, 0, }, + { "SLH-DSA-SHA2-256s", NID_sha512, 0, NID_sha512, 0, }, + { "SLH-DSA-SHAKE-128f", NID_shake128, 0, NID_shake128, 0, }, + { "SLH-DSA-SHAKE-128s", NID_shake128, 0, NID_shake128, 0, }, + { "SLH-DSA-SHAKE-192f", NID_shake256, 0, NID_shake256, 0, }, + { "SLH-DSA-SHAKE-192s", NID_shake256, 0, NID_shake256, 0, }, + { "SLH-DSA-SHAKE-256f", NID_shake256, 0, NID_shake256, 0, }, + { "SLH-DSA-SHAKE-256s", NID_shake256, 0, NID_shake256, 0, }, + { NULL, NID_undef, NID_undef, 0, } /* last */ +}; + +static const EVP_MD *ossl_cms_get_default_md(EVP_PKEY *pk, int *md_a_must) +{ + const EVP_MD *md; + unsigned int i; + int def_nid = NID_undef; + + *md_a_must = 0; + + for (i = 0; key2data[i].name; i++) { + if (EVP_PKEY_is_a(pk, key2data[i].name)) { + def_nid = key2data[i].md_nid; + *md_a_must = key2data[i].md_a_must; + break; + } + } + + if (def_nid == NID_undef && + EVP_PKEY_get_default_digest_nid(pk, &def_nid) <= 0) { + ERR_raise_data(ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST, + "pkey nid=%d", EVP_PKEY_get_id(pk)); + return NULL; + } + md = EVP_get_digestbynid(def_nid); + if (md == NULL) + ERR_raise_data(ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST, + "default md nid=%d", def_nid); + return md; +} + +static const EVP_MD *ossl_cms_get_noattr_md(EVP_PKEY *pk, int *noattr_md_a_must) +{ + unsigned int i; + + for (i = 0; key2data[i].name; i++) { + if (EVP_PKEY_is_a(pk, key2data[i].name)) { + *noattr_md_a_must = key2data[i].noattr_md_a_must; + return EVP_get_digestbynid(key2data[i].noattr_md_nid); + } + } + return NULL; +} + +static int ossl_cms_adjust_md(EVP_PKEY *pk, const EVP_MD **md, unsigned int flags) +{ + const EVP_MD *tmp_md; + int md_a_must = 0; + + while ((flags & CMS_NOATTR) != 0) { + /* + * Some key types require that a certain type of hash is being used in the + * case of signed attributes being absent, others have a suggested hash. + * In the latter case only use the suggested one if no hash is given. + */ + int noattr_md_a_must = 0; + + tmp_md = ossl_cms_get_noattr_md(pk, &noattr_md_a_must); + if (!tmp_md) + break; /* key type not listed - use the default */ + + if (noattr_md_a_must) + *md = tmp_md; + else if (*md == NULL) + *md = tmp_md; + return 1; + } + + tmp_md = ossl_cms_get_default_md(pk, &md_a_must); + if (md_a_must) + *md = tmp_md; + else if (*md == NULL) + *md = tmp_md; + + if (*md == NULL) /* ED448 case */ + return 0; + + return 1; +} + CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, X509 *signer, EVP_PKEY *pk, const EVP_MD *md, unsigned int flags) @@ -398,21 +504,8 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, if (!ossl_cms_set1_SignerIdentifier(si->sid, signer, type, ctx)) goto err; - if (md == NULL) { - int def_nid; - - if (EVP_PKEY_get_default_digest_nid(pk, &def_nid) <= 0) { - ERR_raise_data(ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST, - "pkey nid=%d", EVP_PKEY_get_id(pk)); - goto err; - } - md = EVP_get_digestbynid(def_nid); - if (md == NULL) { - ERR_raise_data(ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST, - "default md nid=%d", def_nid); - goto err; - } - } + if (ossl_cms_adjust_md(pk, &md, flags) != 1) + goto err; X509_ALGOR_set_md(si->digestAlgorithm, md); @@ -427,7 +520,6 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, if (EVP_MD_is_a(md, name)) break; } - if (i == sk_X509_ALGOR_num(sd->digestAlgorithms)) { if ((alg = X509_ALGOR_new()) == NULL) { ERR_raise(ERR_LIB_CMS, ERR_R_ASN1_LIB); @@ -440,7 +532,6 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, goto err; } } - if (!(flags & CMS_KEY_PARAM) && !cms_sd_asn1_ctrl(si, 0)) { ERR_raise_data(ERR_LIB_CMS, CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM, "pkey nid=%d", EVP_PKEY_get_id(pk)); @@ -458,7 +549,6 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, goto err; } } - if (!(flags & CMS_NOSMIMECAP)) { STACK_OF(X509_ALGOR) *smcap = NULL; @@ -518,7 +608,6 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, goto err; } } - if (flags & CMS_KEY_PARAM) { if (flags & CMS_NOATTR) { si->pctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(ctx), @@ -542,20 +631,17 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms, EVP_MD_CTX_set_flags(si->mctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX); } } - if (sd->signerInfos == NULL) sd->signerInfos = sk_CMS_SignerInfo_new_null(); if (sd->signerInfos == NULL || !sk_CMS_SignerInfo_push(sd->signerInfos, si)) { ERR_raise(ERR_LIB_CMS, ERR_R_CRYPTO_LIB); goto err; } - return si; err: M_ASN1_free_of(si, CMS_SignerInfo); return NULL; - } void ossl_cms_SignerInfos_set_cmsctx(CMS_ContentInfo *cms) diff --git a/doc/man3/CMS_add1_signer.pod b/doc/man3/CMS_add1_signer.pod index 3d994889aaa..606df6f6fd6 100644 --- a/doc/man3/CMS_add1_signer.pod +++ b/doc/man3/CMS_add1_signer.pod @@ -82,6 +82,12 @@ bit AES, 128 bit AES, triple DES, 128 bit RC2, 64 bit RC2, DES and 40 bit RC2. If any of these algorithms is not available then it will not be included: for example the GOST algorithms will not be included if the GOST ENGINE is not loaded. +Note that, in the case signedAttributes are not used, for some hash-less signing +schemes the given hash B will be ignored and a hash required by the signing +scheme will be used. This is the case for EdDSA (RFC 8419). For SLH-DSA (RFC 9814) +and ML-DSA (RFC 9882), the scheme-suggested hash will only be used if B is +NULL. + CMS_add1_signer() returns an internal pointer to the CMS_SignerInfo structure just added, this can be used to set additional attributes before it is finalized.