]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
CMS sign digest
authorViktor Söderqvist <viktor.soderqvist@est.tech>
Wed, 28 Apr 2021 08:54:57 +0000 (10:54 +0200)
committerTodd Short <todd.short@me.com>
Sat, 2 Apr 2022 14:42:16 +0000 (10:42 -0400)
CLI changes: New parameter -digest to CLI command openssl cms, to
provide pre-computed digest for use with -sign.

API changes: New function CMS_final_digest(), like CMS_final() but
uses a pre-computed digest instead of computing it from the data.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Todd Short <todd.short@me.com>
(Merged from https://github.com/openssl/openssl/pull/15348)

14 files changed:
apps/cms.c
crypto/cms/cms_err.c
crypto/cms/cms_lib.c
crypto/cms/cms_local.h
crypto/cms/cms_sd.c
crypto/cms/cms_smime.c
crypto/err/openssl.txt
doc/man1/openssl-cms.pod.in
doc/man3/CMS_final.pod
include/crypto/cmserr.h
include/openssl/cms.h.in
include/openssl/cmserr.h
test/recipes/80-test_cms.t
util/libcrypto.num

index 575f8b36251e7347772673a44d595f0b1d1ff7e0..9c15081cd33858b7ab0391ce80bca9ec07eeec74 100644 (file)
@@ -66,7 +66,7 @@ typedef enum OPTION_choice {
     OPT_DECRYPT, OPT_SIGN, OPT_CADES, OPT_SIGN_RECEIPT, OPT_RESIGN,
     OPT_VERIFY, OPT_VERIFY_RETCODE, OPT_VERIFY_RECEIPT,
     OPT_CMSOUT, OPT_DATA_OUT, OPT_DATA_CREATE, OPT_DIGEST_VERIFY,
-    OPT_DIGEST_CREATE, OPT_COMPRESS, OPT_UNCOMPRESS,
+    OPT_DIGEST, OPT_DIGEST_CREATE, OPT_COMPRESS, OPT_UNCOMPRESS,
     OPT_ED_DECRYPT, OPT_ED_ENCRYPT, OPT_DEBUG_DECRYPT, OPT_TEXT,
     OPT_ASCIICRLF, OPT_NOINTERN, OPT_NOVERIFY, OPT_NOCERTS,
     OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP, OPT_BINARY, OPT_KEYID,
@@ -106,6 +106,7 @@ const OPTIONS cms_options[] = {
      "Generate a signed receipt for a message"},
     {"verify_receipt", OPT_VERIFY_RECEIPT, '<',
      "Verify receipts; exit if receipt signatures do not verify"},
+    {"digest", OPT_DIGEST, 's', "Sign a pre-computed digest in hex notation"},
     {"digest_create", OPT_DIGEST_CREATE, '-',
      "Create a CMS \"DigestedData\" object"},
     {"digest_verify", OPT_DIGEST_VERIFY, '-',
@@ -293,6 +294,9 @@ int cms_main(int argc, char **argv)
     const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL;
     char *certsoutfile = NULL, *digestname = NULL, *wrapname = NULL;
     int noCAfile = 0, noCApath = 0, noCAstore = 0;
+    char *digesthex = NULL;
+    unsigned char *digestbin = NULL;
+    long digestlen = 0;
     char *infile = NULL, *outfile = NULL, *rctfile = NULL;
     char *passinarg = NULL, *passin = NULL, *signerfile = NULL;
     char *originatorfile = NULL, *recipfile = NULL, *ciphername = NULL;
@@ -367,6 +371,9 @@ int cms_main(int argc, char **argv)
         case OPT_DIGEST_CREATE:
             operation = SMIME_DIGEST_CREATE;
             break;
+        case OPT_DIGEST:
+            digesthex = opt_arg();
+            break;
         case OPT_DIGEST_VERIFY:
             operation = SMIME_DIGEST_VERIFY;
             break;
@@ -885,10 +892,31 @@ int cms_main(int argc, char **argv)
             goto end;
     }
 
-    in = bio_open_default(infile, 'r',
-                          binary_files ? FORMAT_BINARY : informat);
-    if (in == NULL)
-        goto end;
+    if (digesthex != NULL) {
+        if (operation != SMIME_SIGN) {
+            BIO_printf(bio_err,
+                       "Cannot use -digest for non-signing operation\n");
+            goto end;
+        }
+        if (infile != NULL
+            || (flags & CMS_DETACHED) == 0
+            || (flags & CMS_STREAM) != 0) {
+            BIO_printf(bio_err,
+                       "Cannot use -digest when -in, -nodetach or streaming is used\n");
+            goto end;
+        }
+        digestbin = OPENSSL_hexstr2buf(digesthex, &digestlen);
+        if (digestbin == NULL) {
+            BIO_printf(bio_err,
+                       "Invalid hex value after -digest\n");
+            goto end;
+        }
+    } else {
+        in = bio_open_default(infile, 'r',
+                              binary_files ? FORMAT_BINARY : informat);
+        if (in == NULL)
+            goto end;
+    }
 
     if (operation & SMIME_IP) {
         cms = load_content_info(informat, in, flags, &indata, "SMIME");
@@ -1037,12 +1065,12 @@ int cms_main(int argc, char **argv)
     } else if (operation & SMIME_SIGNERS) {
         int i;
         /*
-         * If detached data content we enable streaming if S/MIME output
-         * format.
+         * If detached data content and not signing pre-computed digest, we
+         * enable streaming if S/MIME output format.
          */
         if (operation == SMIME_SIGN) {
 
-            if (flags & CMS_DETACHED) {
+            if ((flags & CMS_DETACHED) != 0 && digestbin == NULL) {
                 if (outformat == FORMAT_SMIME)
                     flags |= CMS_STREAM;
             }
@@ -1103,7 +1131,12 @@ int cms_main(int argc, char **argv)
             key = NULL;
         }
         /* If not streaming or resigning finalize structure */
-        if ((operation == SMIME_SIGN) && !(flags & CMS_STREAM)) {
+        if (operation == SMIME_SIGN && digestbin != NULL
+            && (flags & CMS_STREAM) == 0) {
+            /* Use pre-computed digest instead of content */
+            if (!CMS_final_digest(cms, digestbin, digestlen, NULL, flags))
+                goto end;
+        } else if (operation == SMIME_SIGN && (flags & CMS_STREAM) == 0) {
             if (!CMS_final(cms, in, NULL, flags))
                 goto end;
         }
@@ -1272,6 +1305,7 @@ int cms_main(int argc, char **argv)
     BIO_free(in);
     BIO_free(indata);
     BIO_free_all(out);
+    OPENSSL_free(digestbin);
     OPENSSL_free(passin);
     NCONF_free(conf);
     return ret;
index 1fba9d8502494dc80719be2931cf783f9e197641..293b65a1f4602a78f03fef1cc6dffae484915f6b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -109,6 +109,8 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_PUBLIC_KEY), "no public key"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_RECEIPT_REQUEST), "no receipt request"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_SIGNERS), "no signers"},
+    {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_OPERATION_UNSUPPORTED),
+    "operation unsupported"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_PEER_KEY_ERROR), "peer key error"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
     "private key does not match certificate"},
index 0b25bc234eb9f470ea2e7dd125aebe8a25aa43cb..4d5a11624fe56e22b1b02c49b7f6b031135a842d 100644 (file)
@@ -207,6 +207,13 @@ err:
 
 /* unfortunately cannot constify SMIME_write_ASN1() due to this function */
 int CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio)
+{
+    return ossl_cms_DataFinal(cms, cmsbio, NULL, 0);
+}
+
+int ossl_cms_DataFinal(CMS_ContentInfo *cms, BIO *cmsbio,
+                       const unsigned char *precomp_md,
+                       unsigned int precomp_mdlen)
 {
     ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
 
@@ -245,7 +252,7 @@ int CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio)
         return ossl_cms_AuthEnvelopedData_final(cms, cmsbio);
 
     case NID_pkcs7_signed:
-        return ossl_cms_SignedData_final(cms, cmsbio);
+        return ossl_cms_SignedData_final(cms, cmsbio, precomp_md, precomp_mdlen);
 
     case NID_pkcs7_digest:
         return ossl_cms_DigestedData_do_final(cms, cmsbio, 0);
index e9fb43715a8a3638a76b29f7572da4c7237db1b5..d44bf3b0a1e70f93addd8775d33f5af022d6d839 100644 (file)
@@ -395,6 +395,9 @@ const char *ossl_cms_ctx_get0_propq(const CMS_CTX *ctx);
 void ossl_cms_resolve_libctx(CMS_ContentInfo *ci);
 
 CMS_ContentInfo *ossl_cms_Data_create(OSSL_LIB_CTX *ctx, const char *propq);
+int ossl_cms_DataFinal(CMS_ContentInfo *cms, BIO *cmsbio,
+                       const unsigned char *precomp_md,
+                       unsigned int precomp_mdlen);
 
 CMS_ContentInfo *ossl_cms_DigestedData_create(const EVP_MD *md,
                                               OSSL_LIB_CTX *libctx,
@@ -404,7 +407,9 @@ int ossl_cms_DigestedData_do_final(const CMS_ContentInfo *cms,
                                    BIO *chain, int verify);
 
 BIO *ossl_cms_SignedData_init_bio(CMS_ContentInfo *cms);
-int ossl_cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain);
+int ossl_cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain,
+                              const unsigned char *precomp_md,
+                              unsigned int precomp_mdlen);
 int ossl_cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert,
                                    int type, const CMS_CTX *ctx);
 int ossl_cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
index d1e5ec8b4e1f99952a2a979d9212b13ea154a7aa..ee210eab295576774068226be1e6b3841c2ca8b8 100644 (file)
@@ -694,7 +694,9 @@ ASN1_OCTET_STRING *CMS_SignerInfo_get0_signature(CMS_SignerInfo *si)
 }
 
 static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
-                                       CMS_SignerInfo *si, BIO *chain)
+                                       CMS_SignerInfo *si, BIO *chain,
+                                       const unsigned char *md,
+                                       unsigned int mdlen)
 {
     EVP_MD_CTX *mctx = EVP_MD_CTX_new();
     int r = 0;
@@ -722,11 +724,13 @@ static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
      */
 
     if (CMS_signed_get_attr_count(si) >= 0) {
-        unsigned char md[EVP_MAX_MD_SIZE];
-        unsigned int mdlen;
+        unsigned char computed_md[EVP_MAX_MD_SIZE];
 
-        if (!EVP_DigestFinal_ex(mctx, md, &mdlen))
-            goto err;
+        if (md == NULL) {
+            if (!EVP_DigestFinal_ex(mctx, computed_md, &mdlen))
+                goto err;
+            md = computed_md;
+        }
         if (!CMS_signed_add1_attr_by_NID(si, NID_pkcs9_messageDigest,
                                          V_ASN1_OCTET_STRING, md, mdlen))
             goto err;
@@ -739,12 +743,14 @@ static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
     } else if (si->pctx) {
         unsigned char *sig;
         size_t siglen;
-        unsigned char md[EVP_MAX_MD_SIZE];
-        unsigned int mdlen;
+        unsigned char computed_md[EVP_MAX_MD_SIZE];
 
         pctx = si->pctx;
-        if (!EVP_DigestFinal_ex(mctx, md, &mdlen))
-            goto err;
+        if (md == NULL) {
+            if (!EVP_DigestFinal_ex(mctx, computed_md, &mdlen))
+                goto err;
+            md = computed_md;
+        }
         siglen = EVP_PKEY_get_size(si->pkey);
         sig = OPENSSL_malloc(siglen);
         if (sig == NULL) {
@@ -760,6 +766,10 @@ static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
         unsigned char *sig;
         unsigned int siglen;
 
+        if (md != NULL) {
+            ERR_raise(ERR_LIB_CMS, CMS_R_OPERATION_UNSUPPORTED);
+            goto err;
+        }
         sig = OPENSSL_malloc(EVP_PKEY_get_size(si->pkey));
         if (sig == NULL) {
             ERR_raise(ERR_LIB_CMS, ERR_R_MALLOC_FAILURE);
@@ -784,7 +794,9 @@ static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
 
 }
 
-int ossl_cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain)
+int ossl_cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain,
+                              const unsigned char *precomp_md,
+                              unsigned int precomp_mdlen)
 {
     STACK_OF(CMS_SignerInfo) *sinfos;
     CMS_SignerInfo *si;
@@ -793,7 +805,7 @@ int ossl_cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain)
     sinfos = CMS_get0_SignerInfos(cms);
     for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
         si = sk_CMS_SignerInfo_value(sinfos, i);
-        if (!cms_SignerInfo_content_sign(cms, si, chain))
+        if (!cms_SignerInfo_content_sign(cms, si, chain, precomp_md, precomp_mdlen))
             return 0;
     }
     cms->d.signedData->encapContentInfo->partial = 0;
index b82eee32cb2157bfbac75cb4ec8022ff458a4d17..690ea3291b12c9d10c97f9285c332d02ed13ff96 100644 (file)
@@ -897,6 +897,31 @@ err:
 
 }
 
+int CMS_final_digest(CMS_ContentInfo *cms,
+                     const unsigned char *md, unsigned int mdlen,
+                     BIO *dcont, unsigned int flags)
+{
+    BIO *cmsbio;
+    int ret = 0;
+
+    if ((cmsbio = CMS_dataInit(cms, dcont)) == NULL) {
+        ERR_raise(ERR_LIB_CMS, CMS_R_CMS_LIB);
+        return 0;
+    }
+
+    (void)BIO_flush(cmsbio);
+
+    if (!ossl_cms_DataFinal(cms, cmsbio, md, mdlen)) {
+        ERR_raise(ERR_LIB_CMS, CMS_R_CMS_DATAFINAL_ERROR);
+        goto err;
+    }
+    ret = 1;
+
+err:
+    do_free_upto(cmsbio, dcont);
+    return ret;
+}
+
 #ifdef ZLIB
 
 int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out,
index c4a94f9559056781bb2efddd2071ee8cec3990c5..27f724723535afdcd902bba30502b91f420a4824 100644 (file)
@@ -343,6 +343,7 @@ CMS_R_NO_PRIVATE_KEY:133:no private key
 CMS_R_NO_PUBLIC_KEY:134:no public key
 CMS_R_NO_RECEIPT_REQUEST:168:no receipt request
 CMS_R_NO_SIGNERS:135:no signers
+CMS_R_OPERATION_UNSUPPORTED:182:operation unsupported
 CMS_R_PEER_KEY_ERROR:188:peer key error
 CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE:136:\
        private key does not match certificate
index c63a7f330ba63600764c814bd8ff10d2f73c09d5..632d7fe8579980342aa65acf7a0148a690b1070b 100644 (file)
@@ -25,6 +25,7 @@ Operation options:
 [B<-resign>]
 [B<-sign_receipt>]
 [B<-verify_receipt> I<receipt>]
+[B<-digest> I<digest>]
 [B<-digest_create>]
 [B<-digest_verify>]
 [B<-compress>]
@@ -207,6 +208,16 @@ Verify a signed receipt in filename B<receipt>. The input message B<must>
 contain the original receipt request. Functionality is otherwise similar
 to the B<-verify> operation.
 
+=item B<-digest> I<digest>
+
+When used with B<-sign>, provides the digest in hexadecimal form instead of
+computing it from the original message content. Cannot be combined with B<-in>
+or B<-nodetach>.
+
+This operation is the CMS equivalent of L<openssl-pkeyutl(1)> signing.
+When signing a pre-computed digest, the security relies on the digest and its
+computation from the original message being trusted.
+
 =item B<-digest_create>
 
 Create a CMS B<DigestedData> type.
@@ -900,6 +911,8 @@ The B<-nameopt> option was added in OpenSSL 3.0.0.
 
 The B<-engine> option was deprecated in OpenSSL 3.0.
 
+The B<-digest> option was added in OpenSSL 3.1.
+
 =head1 COPYRIGHT
 
 Copyright 2008-2021 The OpenSSL Project Authors. All Rights Reserved.
index 8270d53bc712b1a007f45ed41efe696b064af1c3..1ca1d8b092a9f90ed986dc8942dfffa8b160797d 100644 (file)
@@ -2,13 +2,15 @@
 
 =head1 NAME
 
-CMS_final - finalise a CMS_ContentInfo structure
+CMS_final, CMS_final_digest - finalise a CMS_ContentInfo structure
 
 =head1 SYNOPSIS
 
  #include <openssl/cms.h>
 
  int CMS_final(CMS_ContentInfo *cms, BIO *data, BIO *dcont, unsigned int flags);
+ int CMS_final_digest(CMS_ContentInfo *cms, const unsigned char *md,
+                      unsigned int mdlen, BIO *dcont, unsigned int flags);
 
 =head1 DESCRIPTION
 
@@ -19,24 +21,37 @@ processed. The B<dcont> parameter contains a BIO to write content to after
 processing: this is only used with detached data and will usually be set to
 NULL.
 
+CMS_final_digest() finalises the structure B<cms> using a pre-computed digest,
+rather than computing the digest from the original data.
+
 =head1 NOTES
 
-This function will normally be called when the B<CMS_PARTIAL> flag is used. It
+These functions will normally be called when the B<CMS_PARTIAL> flag is used. It
 should only be used when streaming is not performed because the streaming
 I/O functions perform finalisation operations internally.
 
+To sign a pre-computed digest, L<CMS_sign(3)> or CMS_sign_ex() is called
+with the B<data> parameter set to NULL before the CMS structure is finalised
+with the digest provided to CMS_final_digest() in binary form.
+When signing a pre-computed digest, the security relies on the digest and its
+computation from the original message being trusted.
+
 =head1 RETURN VALUES
 
-CMS_final() returns 1 for success or 0 for failure.
+CMS_final() and CMS_final_digest() return 1 for success or 0 for failure.
 
 =head1 SEE ALSO
 
 L<ERR_get_error(3)>, L<CMS_sign(3)>,
 L<CMS_encrypt(3)>
 
+=head1 HISTORY
+
+CMS_final_digest() was added in OpenSSL 3.1.
+
 =head1 COPYRIGHT
 
-Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2008-2021 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index 1de2f9c7d51de95f3bf2c38516cce462622a08c8..a7fcf11fa9571a412d03193b437243769c95c142 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
index da20ddf2f41d1462aa3f95bea84ef944419197aa..e77a9ade6a48f0ff0426e41b7206eb39714a2817 100644 (file)
@@ -119,6 +119,9 @@ int SMIME_write_CMS(BIO *bio, CMS_ContentInfo *cms, BIO *data, int flags);
 
 int CMS_final(CMS_ContentInfo *cms, BIO *data, BIO *dcont,
               unsigned int flags);
+int CMS_final_digest(CMS_ContentInfo *cms,
+                     const unsigned char *md, unsigned int mdlen, BIO *dcont,
+                     unsigned int flags);
 
 CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey,
                           STACK_OF(X509) *certs, BIO *data,
index 1c4f4c799dc3d534cc379229047200d8d507f285..e8fe9d519d14ee05818e6bc7bdd77349d53ec4bc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -86,6 +86,7 @@
 #  define CMS_R_NO_PUBLIC_KEY                              134
 #  define CMS_R_NO_RECEIPT_REQUEST                         168
 #  define CMS_R_NO_SIGNERS                                 135
+#  define CMS_R_OPERATION_UNSUPPORTED                      182
 #  define CMS_R_PEER_KEY_ERROR                             188
 #  define CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE     136
 #  define CMS_R_RECEIPT_DECODE_ERROR                       169
index 48a92f735d5013a3f0846e78a4bec5e44f858914..61d46a0e804d8e5436e48385a9654bd98a18647a 100644 (file)
@@ -50,7 +50,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
 
 $no_rc2 = 1 if disabled("legacy");
 
-plan tests => 12;
+plan tests => 14;
 
 ok(run(test(["pkcs7_test"])), "test pkcs7");
 
@@ -847,6 +847,48 @@ subtest "CMS binary input tests\n" => sub {
        "verify binary input with -binary missing -crlfeol");
 };
 
+subtest "CMS signed digest, DER format" => sub {
+    plan tests => 2;
+
+    # Pre-computed SHA256 digest of $smcont in hexadecimal form
+    my $digest = "ff236ef61b396355f75a4cc6e1c306d4c309084ae271a9e2ad6888f10a101b32";
+
+    my $sig_file = "signature.der";
+    ok(run(app(["openssl", "cms", @prov, "-sign", "-digest", $digest,
+                    "-outform", "DER",
+                    "-certfile", catfile($smdir, "smroot.pem"),
+                    "-signer", catfile($smdir, "smrsa1.pem"),
+                    "-out", $sig_file])),
+        "CMS sign pre-computed digest, DER format");
+
+    ok(run(app(["openssl", "cms", @prov, "-verify", "-in", $sig_file,
+                    "-inform", "DER",
+                    "-CAfile", catfile($smdir, "smroot.pem"),
+                    "-content", $smcont])),
+       "Verify CMS signed digest, DER format");
+};
+
+subtest "CMS signed digest, S/MIME format" => sub {
+    plan tests => 2;
+
+    # Pre-computed SHA256 digest of $smcont in hexadecimal form
+    my $digest = "ff236ef61b396355f75a4cc6e1c306d4c309084ae271a9e2ad6888f10a101b32";
+
+    my $sig_file = "signature.smime";
+    ok(run(app(["openssl", "cms", @prov, "-sign", "-digest", $digest,
+                    "-outform", "SMIME",
+                    "-certfile", catfile($smdir, "smroot.pem"),
+                    "-signer", catfile($smdir, "smrsa1.pem"),
+                    "-out", $sig_file])),
+        "CMS sign pre-computed digest, S/MIME format");
+
+    ok(run(app(["openssl", "cms", @prov, "-verify", "-in", $sig_file,
+                    "-inform", "SMIME",
+                    "-CAfile", catfile($smdir, "smroot.pem"),
+                    "-content", $smcont])),
+       "Verify CMS signed digest, S/MIME format");
+};
+
 sub check_availability {
     my $tnam = shift;
 
index 394f45473287284a0f1dcb087aff2541a3aa35f0..98cc061f30ba1233dd7daa201ff2ad95ecb26893 100644 (file)
@@ -5437,3 +5437,4 @@ BN_signed_bn2native                     ? 3_1_0   EXIST::FUNCTION:
 ASYNC_set_mem_functions                 ?      3_1_0   EXIST::FUNCTION:
 ASYNC_get_mem_functions                 ?      3_1_0   EXIST::FUNCTION:
 BIO_ADDR_dup                            ?      3_1_0   EXIST::FUNCTION:SOCK
+CMS_final_digest                        ?      3_1_0   EXIST::FUNCTION:CMS