]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
cms: Enable signature verification for no-attribute case (hashless signing)
authorStefan Berger <stefanb@linux.ibm.com>
Mon, 13 Oct 2025 18:54:17 +0000 (13:54 -0500)
committerTomas Mraz <tomas@openssl.org>
Tue, 18 Nov 2025 17:03:24 +0000 (18:03 +0100)
Enable signature verification for hashless signing schemes, such as ML-DSA
and EdDSA, for the non-attribute case of CMS. Also in this case the BIO
with the plain input data needs to be passed through to the signature
verification function so that the pure-mode signature verification method
can hash the plain data itself.

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
crypto/cms/cms_smime.c
doc/man3/CMS_verify.pod
include/openssl/cms.h.in
util/libcrypto.num
util/missingcrypto.txt

index f9ec57b26fff491a00062cd9a34efb2ca2aeb232..1eb8dca646c33b75ffa3c9248939f20c154ffb08 100644 (file)
@@ -904,6 +904,41 @@ static int cms_EVP_PKEY_sign(EVP_PKEY_CTX *pctx, BIO *in,
     return ret;
 }
 
+static int cms_EVP_PKEY_verify(EVP_PKEY_CTX *pctx, BIO *in, unsigned char *sig,
+                               size_t siglen, int has_msg_update)
+{
+    size_t buffer_len;
+    int ret;
+
+    if (!has_msg_update) {
+        unsigned char *buffer = NULL;
+
+        if (cms_bio_read(in, &buffer, &buffer_len) != 1)
+            return 0;
+
+        ret = EVP_PKEY_verify(pctx, sig, siglen, buffer, buffer_len);
+        OPENSSL_free(buffer);
+    } else {
+        unsigned char buffer[1024];
+        int n;
+
+        if (BIO_seek(in, 0) < 0)
+            return 0;
+
+        do {
+            n = BIO_read(in, buffer, sizeof(buffer));
+            if (n <= 0)
+                break;
+            if (EVP_PKEY_verify_message_update(pctx, buffer, n) != 1)
+                return 0;
+        } while (!BIO_eof(in));
+        if (EVP_PKEY_CTX_set_signature(pctx, sig, siglen) != 1)
+            return 0;
+        ret = EVP_PKEY_verify_message_final(pctx);
+    }
+    return ret;
+}
+
 static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
                                        CMS_SignerInfo *si, BIO *chain,
                                        BIO *data,
@@ -1239,6 +1274,11 @@ BIO *ossl_cms_SignedData_init_bio(CMS_ContentInfo *cms)
 }
 
 int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
+{
+    return CMS_SignerInfo_verify_ex(si, chain, NULL);
+}
+
+int CMS_SignerInfo_verify_ex(CMS_SignerInfo *si, BIO *chain, BIO *data)
 {
     ASN1_OCTET_STRING *os = NULL;
     EVP_MD_CTX *mctx = EVP_MD_CTX_new();
@@ -1246,6 +1286,8 @@ int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
     int r = -1;
     unsigned char mval[EVP_MAX_MD_SIZE];
     unsigned int mlen;
+    EVP_SIGNATURE *sig_alg = NULL;
+    unsigned char *buffer = NULL;
 
     if (mctx == NULL) {
         ERR_raise(ERR_LIB_CMS, ERR_R_EVP_LIB);
@@ -1286,24 +1328,45 @@ int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
     } else {
         const EVP_MD *md = EVP_MD_CTX_get0_md(mctx);
         const CMS_CTX *ctx = si->cms_ctx;
+        OSSL_LIB_CTX *libctx = ossl_cms_ctx_get0_libctx(ctx);
+        const char *algorithm;
+        int has_msg_update;
 
-        pkctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(ctx),
-                                           si->pkey,
+        pkctx = EVP_PKEY_CTX_new_from_pkey(libctx, si->pkey,
                                            ossl_cms_ctx_get0_propq(ctx));
         if (pkctx == NULL)
             goto err;
-        if (EVP_PKEY_verify_init(pkctx) <= 0)
-            goto err;
-        if (EVP_PKEY_CTX_set_signature_md(pkctx, md) <= 0)
-            goto err;
-        si->pctx = pkctx;
-        if (!cms_sd_asn1_ctrl(si, 1)) {
+
+        if ((algorithm = cms_mdless_signing(si->pkey,
+                                            &has_msg_update)) != NULL) {
+            if (!data) {
+                ERR_raise(ERR_LIB_CMS, CMS_R_NO_CONTENT);
+                goto err;
+            }
+
+            sig_alg = EVP_SIGNATURE_fetch(libctx, algorithm, NULL);
+            if (!sig_alg)
+                goto err;
+            if (EVP_PKEY_verify_message_init(pkctx, sig_alg, NULL) != 1)
+                goto err;
+
+            r = cms_EVP_PKEY_verify(pkctx, data, si->signature->data,
+                                    si->signature->length, has_msg_update);
+        } else {
+            if (EVP_PKEY_verify_init(pkctx) <= 0)
+                goto err;
+            if (EVP_PKEY_CTX_set_signature_md(pkctx, md) <= 0)
+                goto err;
+            si->pctx = pkctx;
+            if (!cms_sd_asn1_ctrl(si, 1)) {
+                si->pctx = NULL;
+                goto err;
+            }
             si->pctx = NULL;
-            goto err;
+
+            r = EVP_PKEY_verify(pkctx, si->signature->data,
+                                si->signature->length, mval, mlen);
         }
-        si->pctx = NULL;
-        r = EVP_PKEY_verify(pkctx, si->signature->data,
-                            si->signature->length, mval, mlen);
         if (r <= 0) {
             ERR_raise(ERR_LIB_CMS, CMS_R_VERIFICATION_FAILURE);
             r = 0;
@@ -1313,6 +1376,8 @@ int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
  err:
     EVP_PKEY_CTX_free(pkctx);
     EVP_MD_CTX_free(mctx);
+    EVP_SIGNATURE_free(sig_alg);
+    OPENSSL_free(buffer);
     return r;
 
 }
index 5be757d2fa9cc93e547025784ace613df4d946d3..cd3cd97ae06334fd7f89643ad343f0c3bc973a5d 100644 (file)
@@ -462,7 +462,7 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
     if (!(flags & CMS_NO_CONTENT_VERIFY)) {
         for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
             si = sk_CMS_SignerInfo_value(sinfos, i);
-            if (CMS_SignerInfo_verify_content(si, cmsbio) <= 0) {
+            if (CMS_SignerInfo_verify_ex(si, cmsbio, tmpin) <= 0) {
                 ERR_raise(ERR_LIB_CMS, CMS_R_CONTENT_VERIFY_ERROR);
                 goto err;
             }
index 9cc0d11818b1266d8a841f4bfa268e9bef2a8cf2..bef4858c918e6d3cfa0444864999cba21203714b 100644 (file)
@@ -2,8 +2,9 @@
 
 =head1 NAME
 
-CMS_verify, CMS_SignedData_verify,
-CMS_get0_signers - verify a CMS SignedData structure
+CMS_verify, CMS_SignedData_verify, CMS_get0_signers, CMS_SignerInfo_verify,
+CMS_SignerInfo_verify_content, CMS_SignerInfo_verify_ex
+- verify CMS SignedData and SignedInfo structures
 
 =head1 SYNOPSIS
 
@@ -19,6 +20,11 @@ CMS_get0_signers - verify a CMS SignedData structure
 
  STACK_OF(X509) *CMS_get0_signers(CMS_ContentInfo *cms);
 
+ int CMS_SignerInfo_verify(CMS_SignerInfo *si);
+ int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain);
+ int CMS_SignerInfo_verify_ex(CMS_SignerInfo *si, BIO *chain,
+                                    BIO *data);
+
 =head1 DESCRIPTION
 
 CMS_verify() is very similar to L<PKCS7_verify(3)>. It verifies a
@@ -52,6 +58,21 @@ are used when retrieving algorithms from providers.
 CMS_get0_signers() retrieves the signing certificate(s) from I<cms>; it may only
 be called after a successful CMS_verify() or CMS_SignedData_verify() operation.
 
+CMS_SignerInfo_verify() verifies the signed attributes attached to the given
+SignerInfo B<si>.
+
+CMS_SignerInfo_verify_content() verifies the SignerInfo B<si> part of a
+CMS_ContentInfo with the data provided by the B<cmsbio> BIO. This BIO will be
+used by hash-based signing schemes and must return the same hash value as was
+produced by the B<cmsbio> of CMS_dataFinal() or CMS_dataFinal_ex().
+
+CMS_SignerInfo_verify_ex() extends CMS_SignerInfo_verify_content() with support
+for hash-less signing schemes, such as ML-DSA, SLH-DSA, or EdDSA, that require
+access to the raw (non-hashed) data. The raw data must be provided by the
+B<data> BIO. Note that this BIO must support the seek() function so that its
+data stream can be read multiple times, once for each signature created by a
+hash-less signing scheme.
+
 =head1 VERIFY PROCESS
 
 Normally the verify process proceeds as follows.
index 04d9325873a7d2c01283fdd0da953e3ed4099480..c681d412035e963e82f288dac040dc1cc08a30cc 100644 (file)
@@ -302,6 +302,7 @@ ASN1_OCTET_STRING *CMS_SignerInfo_get0_signature(CMS_SignerInfo *si);
 int CMS_SignerInfo_sign(CMS_SignerInfo *si);
 int CMS_SignerInfo_verify(CMS_SignerInfo *si);
 int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain);
+int CMS_SignerInfo_verify_ex(CMS_SignerInfo *si, BIO *chain, BIO *data);
 BIO *CMS_SignedData_verify(CMS_SignedData *sd, BIO *detached_data,
                            STACK_OF(X509) *scerts, X509_STORE *store,
                            STACK_OF(X509) *extra, STACK_OF(X509_CRL) *crls,
index 0b497859e15365945973bef5cd9174de589be1b6..67aa2cb5b9ca1e740985efd6011b6893c9337a35 100644 (file)
@@ -5944,3 +5944,4 @@ OPENSSL_tm_to_posix                     ? 4_0_0   EXIST::FUNCTION:
 OPENSSL_timegm                          ?      4_0_0   EXIST::FUNCTION:
 OSSL_PARAM_clear_free                   ?      4_0_0   EXIST::FUNCTION:
 CMS_dataFinal_ex                        ?      4_0_0   EXIST::FUNCTION:CMS
+CMS_SignerInfo_verify_ex                ?      4_0_0   EXIST::FUNCTION:CMS
index 3aaab8bc47185c48b8919acc247faefad7ce38d5..873e7a774505b79d622426e1e89a4ab69521be7e 100644 (file)
@@ -299,8 +299,6 @@ CMS_SignedData_init(3)
 CMS_SignerInfo_get0_algs(3)
 CMS_SignerInfo_get0_md_ctx(3)
 CMS_SignerInfo_get0_pkey_ctx(3)
-CMS_SignerInfo_verify(3)
-CMS_SignerInfo_verify_content(3)
 CMS_add0_CertificateChoices(3)
 CMS_add0_RevocationInfoChoice(3)
 CMS_add0_recipient_password(3)