]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
cms: Get a default hash for hash-less signing schemes
authorStefan Berger <stefanb@linux.ibm.com>
Tue, 14 Oct 2025 22:52:45 +0000 (17:52 -0500)
committerTomas Mraz <tomas@openssl.org>
Tue, 18 Nov 2025 17:03:24 +0000 (18:03 +0100)
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 <stefanb@linux.ibm.com>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/28923)

crypto/cms/cms_sd.c
doc/man3/CMS_add1_signer.pod

index 80f7d73d6dd09b30e25b0f17201089c6b54fb37e..7f7059df28983a8dfe8b52507fd3d4a04fdfb362 100644 (file)
@@ -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)
index 3d994889aaaba7c521703c4f73198440cacaf5fa..606df6f6fd6f7f4875ac7b3b0fa3eb45e3b3ac05 100644 (file)
@@ -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<md> 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<md> 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.