From: Stefan Berger Date: Mon, 13 Oct 2025 18:54:17 +0000 (-0500) Subject: cms: Enable signature verification for no-attribute case (hashless signing) X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=266fb55eb8b3b7bfdcd04d757e751b4a26c9ad06;p=thirdparty%2Fopenssl.git cms: Enable signature verification for no-attribute case (hashless signing) 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 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 f9ec57b26ff..1eb8dca646c 100644 --- a/crypto/cms/cms_sd.c +++ b/crypto/cms/cms_sd.c @@ -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; } diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 5be757d2fa9..cd3cd97ae06 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -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; } diff --git a/doc/man3/CMS_verify.pod b/doc/man3/CMS_verify.pod index 9cc0d11818b..bef4858c918 100644 --- a/doc/man3/CMS_verify.pod +++ b/doc/man3/CMS_verify.pod @@ -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. It verifies a @@ -52,6 +58,21 @@ are used when retrieving algorithms from providers. CMS_get0_signers() retrieves the signing certificate(s) from I; 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. + +CMS_SignerInfo_verify_content() verifies the SignerInfo B part of a +CMS_ContentInfo with the data provided by the B BIO. This BIO will be +used by hash-based signing schemes and must return the same hash value as was +produced by the B 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 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. diff --git a/include/openssl/cms.h.in b/include/openssl/cms.h.in index 04d9325873a..c681d412035 100644 --- a/include/openssl/cms.h.in +++ b/include/openssl/cms.h.in @@ -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, diff --git a/util/libcrypto.num b/util/libcrypto.num index 0b497859e15..67aa2cb5b9c 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -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 diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 3aaab8bc471..873e7a77450 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -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)