]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
crypto/cms: add CAdES-BES signed attributes validation
authorFdaSilvaYY <fdasilvayy@gmail.com>
Wed, 12 Jun 2019 17:52:39 +0000 (19:52 +0200)
committerTomas Mraz <tmraz@fedoraproject.org>
Wed, 27 May 2020 11:16:30 +0000 (13:16 +0200)
for signing certificate V2 and signing certificate extensions.

CAdES: lowercase name for now internal methods.

crypto/cms: generated file changes.

Add some CHANGES entries.

[extended tests]

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/8098)

19 files changed:
CHANGES.md
apps/cms.c
crypto/cms/cms_err.c
crypto/cms/cms_ess.c
crypto/cms/cms_local.h
crypto/cms/cms_smime.c
crypto/err/openssl.txt
crypto/ess/build.info
crypto/ess/ess_asn1.c
crypto/ess/ess_err.c
crypto/ess/ess_lib.c
crypto/ts/ts_rsp_verify.c
doc/man1/openssl-cms.pod.in
doc/man3/CMS_verify.pod
include/crypto/cms.h
include/crypto/ess.h
include/openssl/cmserr.h
include/openssl/esserr.h
test/recipes/80-test_cms.t

index eb8659e9cfa1b0e22b6ee87efc2b3f9360e23766..10fd8d541db1946d0eb48bf6ccf0f0ee0183fbcf 100644 (file)
@@ -23,6 +23,15 @@ OpenSSL 3.0
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx]
 
+ * Add CAdES-BES signature verification support, mostly derived
+   from ESSCertIDv2 TS (RFC 5816) contribution by Marek Klein.
+
+   *Filipe Raimundo da Silva*
+
+ * Add CAdES-BES signature scheme and attributes support (RFC 5126) to CMS API.
+
+   *Antonio Iacono*
+
  * Deprecated EC_POINT_make_affine() and EC_POINTs_make_affine(). These
    functions are not widely used and now OpenSSL automatically perform this
    conversion when needed.
index 6b5577eceef39da084f734f16524283bbef9895d..445fec53884c3a290731dfd9bad66b0ae3976e07 100644 (file)
@@ -670,12 +670,18 @@ int cms_main(int argc, char **argv)
         goto opthelp;
     }
 
-    if (flags & CMS_CADES) {
-        if (flags & CMS_NOATTR) {
+    if ((flags & CMS_CADES) != 0) {
+        if ((flags & CMS_NOATTR) != 0) {
             BIO_puts(bio_err, "Incompatible options: "
                      "CAdES required signed attributes\n");
             goto opthelp;
         }
+        if (operation == SMIME_VERIFY
+                && (flags & (CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY)) != 0) {
+            BIO_puts(bio_err, "Incompatible options: CAdES validation require"
+                     " certs and signed attributes validations\n");
+            goto opthelp;
+        }
     }
 
     if (operation & SMIME_SIGNERS) {
@@ -1115,7 +1121,8 @@ int cms_main(int argc, char **argv)
             goto end;
     } else if (operation == SMIME_VERIFY) {
         if (CMS_verify(cms, other, store, indata, out, flags) > 0) {
-            BIO_printf(bio_err, "Verification successful\n");
+            BIO_printf(bio_err, "%s Verification successful\n",
+                       (flags & CMS_CADES) ? "CAdES" : "CMS");
         } else {
             BIO_printf(bio_err, "Verification failure\n");
             if (verify_retcode)
index 526d77357ef904d134320bad8454ce994ec81713..16e25afc7f77e25b31ceec934b79a53383c6b7d4 100644 (file)
@@ -52,6 +52,10 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_ERROR_SETTING_KEY), "error setting key"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_ERROR_SETTING_RECIPIENTINFO),
     "error setting recipientinfo"},
+    {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_ESS_NO_SIGNING_CERTID_ATTRIBUTE),
+    "ess no signing certid attribute"},
+    {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR),
+    "ess signing certid mismatch error"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_ENCRYPTED_KEY_LENGTH),
     "invalid encrypted key length"},
     {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER),
index 3901074033134fe72738bf783bdbec0ee4fd98be..e3604f7db8ae8cc35730ababb97a995bc06a4f7c 100644 (file)
@@ -21,6 +21,9 @@
 
 DEFINE_STACK_OF(GENERAL_NAMES)
 DEFINE_STACK_OF(CMS_SignerInfo)
+DEFINE_STACK_OF(ESS_CERT_ID)
+DEFINE_STACK_OF(ESS_CERT_ID_V2)
+DEFINE_STACK_OF(X509)
 
 IMPLEMENT_ASN1_FUNCTIONS(CMS_ReceiptRequest)
 
@@ -29,33 +32,100 @@ IMPLEMENT_ASN1_FUNCTIONS(CMS_ReceiptRequest)
 int CMS_get1_ReceiptRequest(CMS_SignerInfo *si, CMS_ReceiptRequest **prr)
 {
     ASN1_STRING *str;
-    CMS_ReceiptRequest *rr = NULL;
-    if (prr)
+    CMS_ReceiptRequest *rr;
+    ASN1_OBJECT *obj = OBJ_nid2obj(NID_id_smime_aa_receiptRequest);
+
+    if (prr != NULL)
         *prr = NULL;
-    str = CMS_signed_get0_data_by_OBJ(si,
-                                      OBJ_nid2obj
-                                      (NID_id_smime_aa_receiptRequest), -3,
-                                      V_ASN1_SEQUENCE);
-    if (!str)
+    str = CMS_signed_get0_data_by_OBJ(si, obj, -3, V_ASN1_SEQUENCE);
+    if (str == NULL)
         return 0;
 
     rr = ASN1_item_unpack(str, ASN1_ITEM_rptr(CMS_ReceiptRequest));
-    if (!rr)
+    if (rr == NULL)
         return -1;
-    if (prr)
+    if (prr != NULL)
         *prr = rr;
     else
         CMS_ReceiptRequest_free(rr);
     return 1;
 }
 
+/*
+    First, get the ESS_SIGNING_CERT(V2) signed attribute from |si|.
+    Then check matching of each cert of trust |chain| with one of 
+    the |cert_ids|(Hash+IssuerID) list from this ESS_SIGNING_CERT.
+    Derived from ts_check_signing_certs()
+*/
+int ess_check_signing_certs(CMS_SignerInfo *si, STACK_OF(X509) *chain)
+{
+    ESS_SIGNING_CERT *ss = NULL;
+    ESS_SIGNING_CERT_V2 *ssv2 = NULL;
+    X509 *cert;
+    int i = 0, ret = 0;
+
+    if (cms_signerinfo_get_signing_cert(si, &ss) > 0 && ss->cert_ids != NULL) {
+        STACK_OF(ESS_CERT_ID) *cert_ids = ss->cert_ids;
+
+        cert = sk_X509_value(chain, 0);
+        if (ess_find_cert(cert_ids, cert) != 0)
+            goto err;
+
+        /*
+         * Check the other certificates of the chain.
+         * Fail if no signing certificate ids found for each certificate.
+         */
+        if (sk_ESS_CERT_ID_num(cert_ids) > 1) {
+            /* for each chain cert, try to find its cert id */
+            for (i = 1; i < sk_X509_num(chain); ++i) {
+                cert = sk_X509_value(chain, i);
+                if (ess_find_cert(cert_ids, cert) < 0)
+                    goto err;
+            }
+        }
+    } else if (cms_signerinfo_get_signing_cert_v2(si, &ssv2) > 0
+                   && ssv2->cert_ids!= NULL) {
+        STACK_OF(ESS_CERT_ID_V2) *cert_ids_v2 = ssv2->cert_ids;
+
+        cert = sk_X509_value(chain, 0);
+        if (ess_find_cert_v2(cert_ids_v2, cert) != 0)
+            goto err;
+
+        /*
+         * Check the other certificates of the chain.
+         * Fail if no signing certificate ids found for each certificate.
+         */
+        if (sk_ESS_CERT_ID_V2_num(cert_ids_v2) > 1) {
+            /* for each chain cert, try to find its cert id */
+            for (i = 1; i < sk_X509_num(chain); ++i) {
+                cert = sk_X509_value(chain, i);
+                if (ess_find_cert_v2(cert_ids_v2, cert) < 0)
+                    goto err;
+            }
+        }
+    } else {
+        CMSerr(CMS_F_ESS_CHECK_SIGNING_CERTS,
+               CMS_R_ESS_NO_SIGNING_CERTID_ATTRIBUTE);
+        return 0;
+    }
+    ret = 1;
+ err:
+    if (!ret)
+        CMSerr(CMS_F_ESS_CHECK_SIGNING_CERTS,
+               CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR);
+
+    ESS_SIGNING_CERT_free(ss);
+    ESS_SIGNING_CERT_V2_free(ssv2);
+    return ret;
+}
+
 CMS_ReceiptRequest *CMS_ReceiptRequest_create0(unsigned char *id, int idlen,
                                                int allorfirst,
                                                STACK_OF(GENERAL_NAMES)
                                                *receiptList, STACK_OF(GENERAL_NAMES)
                                                *receiptsTo)
 {
-    CMS_ReceiptRequest *rr = NULL;
+    CMS_ReceiptRequest *rr;
 
     rr = CMS_ReceiptRequest_new();
     if (rr == NULL)
@@ -145,6 +215,7 @@ static int cms_msgSigDigest(CMS_SignerInfo *si,
                             unsigned char *dig, unsigned int *diglen)
 {
     const EVP_MD *md;
+
     md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
     if (md == NULL)
         return 0;
@@ -160,6 +231,7 @@ int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src)
 {
     unsigned char dig[EVP_MAX_MD_SIZE];
     unsigned int diglen;
+
     if (!cms_msgSigDigest(src, dig, &diglen)) {
         CMSerr(CMS_F_CMS_MSGSIGDIGEST_ADD1, CMS_R_MSGSIGDIGEST_ERROR);
         return 0;
index 00ea9006c8e4f89f871e3d99e0dfe947b560167f..68c885622b7f1bc27e83adc59534b15ca52a89ce 100644 (file)
@@ -421,6 +421,9 @@ int cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms, CMS_RecipientInfo *
 /* SignerInfo routines */
 int CMS_si_check_attributes(const CMS_SignerInfo *si);
 
+/* ESS routines */
+int ess_check_signing_certs(CMS_SignerInfo *si, STACK_OF(X509) *chain);
+
 DECLARE_ASN1_ITEM(CMS_CertificateChoices)
 DECLARE_ASN1_ITEM(CMS_DigestedData)
 DECLARE_ASN1_ITEM(CMS_EncryptedData)
index dbdc815e9729b75459491fcb08a3a40797657a99..a83edce0f7e5e960db09f6c6781e60fd1373bfa8 100644 (file)
@@ -233,7 +233,8 @@ CMS_ContentInfo *CMS_EncryptedData_encrypt(BIO *in, const EVP_CIPHER *cipher,
 static int cms_signerinfo_verify_cert(CMS_SignerInfo *si,
                                       X509_STORE *store,
                                       STACK_OF(X509) *certs,
-                                      STACK_OF(X509_CRL) *crls)
+                                      STACK_OF(X509_CRL) *crls,
+                                      STACK_OF(X509) **chain)
 {
     X509_STORE_CTX *ctx = X509_STORE_CTX_new();
     X509 *signer;
@@ -262,6 +263,10 @@ static int cms_signerinfo_verify_cert(CMS_SignerInfo *si,
         goto err;
     }
     r = 1;
+
+    /* also send back the trust chain when required */
+    if (chain != NULL)
+        *chain = X509_STORE_CTX_get1_chain(ctx);
  err:
     X509_STORE_CTX_free(ctx);
     return r;
@@ -275,9 +280,11 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
     STACK_OF(CMS_SignerInfo) *sinfos;
     STACK_OF(X509) *cms_certs = NULL;
     STACK_OF(X509_CRL) *crls = NULL;
+    STACK_OF(X509) **si_chains = NULL;
     X509 *signer;
     int i, scount = 0, ret = 0;
     BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL;
+    int cadesVerify = (flags & CMS_CADES) != 0;
 
     if (!dcont && !check_content(cms))
         return 0;
@@ -312,27 +319,44 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
     }
 
     /* Attempt to verify all signers certs */
-
-    if (!(flags & CMS_NO_SIGNER_CERT_VERIFY)) {
+    /* at this point scount == sk_CMS_SignerInfo_num(sinfos) */
+
+    if ((flags & CMS_NO_SIGNER_CERT_VERIFY) == 0 || cadesVerify) {
+        if (cadesVerify) {
+            /* Certificate trust chain is required to check CAdES signature */
+            si_chains = OPENSSL_zalloc(scount * sizeof(si_chains[0]));
+            if (si_chains == NULL) {
+                CMSerr(CMS_F_CMS_VERIFY, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
         cms_certs = CMS_get1_certs(cms);
         if (!(flags & CMS_NOCRL))
             crls = CMS_get1_crls(cms);
-        for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+        for (i = 0; i < scount; i++) {
             si = sk_CMS_SignerInfo_value(sinfos, i);
-            if (!cms_signerinfo_verify_cert(si, store, cms_certs, crls))
+
+            if (!cms_signerinfo_verify_cert(si, store, cms_certs, crls,
+                                            si_chains ? &si_chains[i] : NULL))
                 goto err;
         }
     }
 
     /* Attempt to verify all SignerInfo signed attribute signatures */
 
-    if (!(flags & CMS_NO_ATTR_VERIFY)) {
-        for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+    if ((flags & CMS_NO_ATTR_VERIFY) == 0 || cadesVerify) {
+        for (i = 0; i < scount; i++) {
             si = sk_CMS_SignerInfo_value(sinfos, i);
             if (CMS_signed_get_attr_count(si) < 0)
                 continue;
             if (CMS_SignerInfo_verify(si) <= 0)
                 goto err;
+            if (cadesVerify) {
+                STACK_OF(X509) *si_chain = si_chains ? si_chains[i] : NULL;
+
+                if (ess_check_signing_certs(si, si_chain) <= 0)
+                    goto err;
+            }
         }
     }
 
@@ -420,6 +444,11 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
         BIO_free_all(tmpout);
 
  err2:
+    if (si_chains != NULL) {
+        for (i = 0; i < scount; ++i)
+            sk_X509_pop_free(si_chains[i], X509_free);
+        OPENSSL_free(si_chains);
+    }
     sk_X509_pop_free(cms_certs, X509_free);
     sk_X509_CRL_pop_free(crls, X509_CRL_free);
 
index 1b2c94b0a26e92bdaf7333906284b91eee32be05..8afc67e747d69da4079ce404b7a554066870a873 100644 (file)
@@ -320,6 +320,7 @@ CMS_F_CMS_SET_DETACHED:147:CMS_set_detached
 CMS_F_CMS_SIGN:148:CMS_sign
 CMS_F_CMS_SIGNED_DATA_INIT:149:cms_signed_data_init
 CMS_F_CMS_SIGNERINFO_CONTENT_SIGN:150:cms_SignerInfo_content_sign
+CMS_F_CMS_SIGNERINFO_GET_CHAIN:184:cms_signerinfo_get_chain
 CMS_F_CMS_SIGNERINFO_SIGN:151:CMS_SignerInfo_sign
 CMS_F_CMS_SIGNERINFO_VERIFY:152:CMS_SignerInfo_verify
 CMS_F_CMS_SIGNERINFO_VERIFY_CERT:153:cms_signerinfo_verify_cert
@@ -329,6 +330,7 @@ CMS_F_CMS_SI_CHECK_ATTRIBUTES:183:CMS_si_check_attributes
 CMS_F_CMS_STREAM:155:CMS_stream
 CMS_F_CMS_UNCOMPRESS:156:CMS_uncompress
 CMS_F_CMS_VERIFY:157:CMS_verify
+CMS_F_ESS_CHECK_SIGNING_CERTS:185:ess_check_signing_certs
 CMS_F_KEK_UNWRAP_KEY:180:kek_unwrap_key
 COMP_F_BIO_ZLIB_FLUSH:99:bio_zlib_flush
 COMP_F_BIO_ZLIB_NEW:100:bio_zlib_new
@@ -2188,6 +2190,8 @@ CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE:114:\
        error reading messagedigest attribute
 CMS_R_ERROR_SETTING_KEY:115:error setting key
 CMS_R_ERROR_SETTING_RECIPIENTINFO:116:error setting recipientinfo
+CMS_R_ESS_NO_SIGNING_CERTID_ATTRIBUTE:182:ess no signing certid attribute
+CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR:183:ess signing certid mismatch error
 CMS_R_INVALID_ENCRYPTED_KEY_LENGTH:117:invalid encrypted key length
 CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER:176:invalid key encryption parameter
 CMS_R_INVALID_KEY_LENGTH:118:invalid key length
index 24fcecc8f5a264135c2d67083a7d132cdfcb747e..fa2bc41d08cc1147802c58271687658a3f71736e 100644 (file)
@@ -1,3 +1,8 @@
 LIBS=../../libcrypto
-SOURCE[../../libcrypto]= \
-        ess_lib.c ess_asn1.c ess_err.c
+
+IF[{- !$disabled{'cms'} and !$disabled{'ts'} -}]
+  SOURCE[../../libcrypto]= ess_lib.c
+ENDIF
+
+SOURCE[../../libcrypto]= ess_asn1.c ess_err.c 
+
index 19589d97f3c047f31ddc213533de60c9b88b1ce6..a8d13a3a20169bdb5a2b1e74b3429d0ce39806e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2020 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
@@ -9,9 +9,11 @@
 
 #include <openssl/err.h>
 #include <openssl/asn1t.h>
+#include <openssl/cms.h>
 #include <openssl/ess.h>
 #include <openssl/x509v3.h>
 #include "crypto/ess.h"
+#include "crypto/cms.h"
 
 /* ASN1 stuff for ESS Structure */
 
@@ -55,3 +57,61 @@ ASN1_SEQUENCE(ESS_SIGNING_CERT_V2) = {
 
 IMPLEMENT_ASN1_FUNCTIONS(ESS_SIGNING_CERT_V2)
 IMPLEMENT_ASN1_DUP_FUNCTION(ESS_SIGNING_CERT_V2)
+
+/* No cms support means no CMS_SignerInfo* definitions */
+#ifndef OPENSSL_NO_CMS
+
+/*
+ * Returns < 0 if attribute is not found, 1 if found, or 
+ * -1 on attribute parsing failure.
+ */
+int cms_signerinfo_get_signing_cert_v2(CMS_SignerInfo *si,
+                                       ESS_SIGNING_CERT_V2 **psc)
+{
+    ASN1_STRING *str;
+    ESS_SIGNING_CERT_V2 *sc;
+    ASN1_OBJECT *obj = OBJ_nid2obj(NID_id_smime_aa_signingCertificateV2);
+
+    if (psc != NULL)
+        *psc = NULL;
+    str = CMS_signed_get0_data_by_OBJ(si, obj, -3, V_ASN1_SEQUENCE);
+    if (str == NULL)
+        return 0;
+
+    sc = ASN1_item_unpack(str, ASN1_ITEM_rptr(ESS_SIGNING_CERT_V2));
+    if (sc == NULL)
+        return -1;
+    if (psc != NULL)
+        *psc = sc;
+    else
+        ESS_SIGNING_CERT_V2_free(sc);
+    return 1;
+}
+
+/*
+ * Returns < 0 if attribute is not found, 1 if found, or 
+ * -1 on attribute parsing failure.
+ */
+int cms_signerinfo_get_signing_cert(CMS_SignerInfo *si,
+                                    ESS_SIGNING_CERT **psc)
+{
+    ASN1_STRING *str;
+    ESS_SIGNING_CERT *sc;
+    ASN1_OBJECT *obj = OBJ_nid2obj(NID_id_smime_aa_signingCertificate);
+
+    if (psc != NULL)
+        *psc = NULL;
+    str = CMS_signed_get0_data_by_OBJ(si, obj, -3, V_ASN1_SEQUENCE);
+    if (str == NULL)
+        return 0;
+
+    sc = ASN1_item_unpack(str, ASN1_ITEM_rptr(ESS_SIGNING_CERT));
+    if (sc == NULL)
+        return -1;
+    if (psc != NULL)
+        *psc = sc;
+    else
+        ESS_SIGNING_CERT_free(sc);
+    return 1;
+}
+#endif  /* !OPENSSL_NO_CMS */
index b494aa246f5afe0bd4eac629e4400f60cbde0478..6547645fea6365586bb233b4de522dafa0e11096 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2020 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 9d9defa9d7746ba1b001e2ebc8cd078f9e639207..17f9db98ffaa7d82e893dda8e3ead06864bda53e 100644 (file)
@@ -15,8 +15,8 @@
 
 DEFINE_STACK_OF(ESS_CERT_ID)
 DEFINE_STACK_OF(ESS_CERT_ID_V2)
-DEFINE_STACK_OF(X509)
 DEFINE_STACK_OF(GENERAL_NAME)
+DEFINE_STACK_OF(X509)
 
 static ESS_CERT_ID *ESS_CERT_ID_new_init(X509 *cert, int issuer_needed);
 static ESS_CERT_ID_V2 *ESS_CERT_ID_V2_new_init(const EVP_MD *hash_alg,
@@ -61,9 +61,12 @@ static ESS_CERT_ID *ESS_CERT_ID_new_init(X509 *cert, int issuer_needed)
     unsigned char cert_sha1[SHA_DIGEST_LENGTH];
 
     /* Call for side-effect of computing hash and caching extensions */
-    X509_check_purpose(cert, -1, 0);
+    if (!X509v3_cache_extensions(cert, NULL, NULL))
+        return NULL;
+
     if ((cid = ESS_CERT_ID_new()) == NULL)
         goto err;
+    /* TODO(3.0): fetch sha1 algorithm from providers */
     if (!X509_digest(cert, EVP_sha1(), cert_sha1, NULL))
         goto err;
     if (!ASN1_OCTET_STRING_set(cid->hash, cert_sha1, SHA_DIGEST_LENGTH))
@@ -85,8 +88,8 @@ static ESS_CERT_ID *ESS_CERT_ID_new_init(X509 *cert, int issuer_needed)
         goto err;
     name = NULL;            /* Ownership is lost. */
     ASN1_INTEGER_free(cid->issuer_serial->serial);
-    if (!(cid->issuer_serial->serial =
-          ASN1_INTEGER_dup(X509_get_serialNumber(cert))))
+    if ((cid->issuer_serial->serial =
+          ASN1_INTEGER_dup(X509_get_serialNumber(cert))) == NULL)
         goto err;
 
     return cid;
@@ -159,6 +162,7 @@ static ESS_CERT_ID_V2 *ESS_CERT_ID_V2_new_init(const EVP_MD *hash_alg,
         cid->hash_alg = NULL;
     }
 
+    /* TODO(3.0): fetch sha1 algorithm from providers */
     if (!X509_digest(cert, hash_alg, hash, &hash_len))
         goto err;
 
@@ -196,8 +200,9 @@ ESS_SIGNING_CERT *ESS_SIGNING_CERT_get(PKCS7_SIGNER_INFO *si)
 {
     ASN1_TYPE *attr;
     const unsigned char *p;
+
     attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificate);
-    if (!attr)
+    if (attr == NULL)
         return NULL;
     p = attr->value.sequence->data;
     return d2i_ESS_SIGNING_CERT(NULL, &p, attr->value.sequence->length);
@@ -273,3 +278,86 @@ int ESS_SIGNING_CERT_V2_add(PKCS7_SIGNER_INFO *si,
     OPENSSL_free(pp);
     return 0;
 }
+
+static int ess_issuer_serial_cmp(const ESS_ISSUER_SERIAL *is, const X509 *cert)
+{
+    GENERAL_NAME *issuer;
+
+    if (is == NULL || cert == NULL || sk_GENERAL_NAME_num(is->issuer) != 1)
+        return -1;
+
+    issuer = sk_GENERAL_NAME_value(is->issuer, 0);
+    if (issuer->type != GEN_DIRNAME
+        || X509_NAME_cmp(issuer->d.dirn, X509_get_issuer_name(cert)) != 0)
+        return -1;
+
+    return ASN1_INTEGER_cmp(is->serial, X509_get0_serialNumber(cert));
+}
+
+/* Returns < 0 if certificate is not found, certificate index otherwise. */
+int ess_find_cert(const STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert)
+{
+    int i;
+    unsigned char cert_sha1[SHA_DIGEST_LENGTH];
+
+    if (cert_ids == NULL || cert == NULL)
+        return -1;
+
+    /* Recompute SHA1 hash of certificate if necessary (side effect). */
+    if (!X509v3_cache_extensions(cert, NULL, NULL))
+        return -1;
+
+    /* TODO(3.0): fetch sha1 algorithm from providers */
+    if (!X509_digest(cert, EVP_sha1(), cert_sha1, NULL))
+        return -1;
+
+    /* Look for cert in the cert_ids vector. */
+    for (i = 0; i < sk_ESS_CERT_ID_num(cert_ids); ++i) {
+        const ESS_CERT_ID *cid = sk_ESS_CERT_ID_value(cert_ids, i);
+
+        if (cid->hash->length == SHA_DIGEST_LENGTH
+            && memcmp(cid->hash->data, cert_sha1, SHA_DIGEST_LENGTH) == 0) {
+            const ESS_ISSUER_SERIAL *is = cid->issuer_serial;
+
+            if (is == NULL || ess_issuer_serial_cmp(is, cert) == 0)
+                return i;
+        }
+    }
+
+    return -1;
+}
+
+/* Returns < 0 if certificate is not found, certificate index otherwise. */
+int ess_find_cert_v2(const STACK_OF(ESS_CERT_ID_V2) *cert_ids, const X509 *cert)
+{
+    int i;
+    unsigned char cert_digest[EVP_MAX_MD_SIZE];
+    unsigned int len;
+
+    /* Look for cert in the cert_ids vector. */
+    for (i = 0; i < sk_ESS_CERT_ID_V2_num(cert_ids); ++i) {
+        const ESS_CERT_ID_V2 *cid = sk_ESS_CERT_ID_V2_value(cert_ids, i);
+        const EVP_MD *md;
+
+        if (cid != NULL && cid->hash_alg != NULL)
+            md = EVP_get_digestbyobj(cid->hash_alg->algorithm);
+        else
+            md = EVP_sha256();
+
+        /* TODO(3.0): fetch sha1 algorithm from providers */
+        if (!X509_digest(cert, md, cert_digest, &len))
+            return -1;
+
+        if (cid->hash->length != (int)len)
+            return -1;
+
+        if (memcmp(cid->hash->data, cert_digest, cid->hash->length) == 0) {
+            const ESS_ISSUER_SERIAL *is = cid->issuer_serial;
+
+            if (is == NULL || ess_issuer_serial_cmp(is, cert) == 0)
+                return i;
+        }
+    }
+
+    return -1;
+}
index b872f75bea7f1832580e565cc7c76df0868dd845..c909b211d46734cd1e350279c75d08ddd3e20b7b 100644 (file)
@@ -26,8 +26,7 @@ static int ts_verify_cert(X509_STORE *store, STACK_OF(X509) *untrusted,
                           X509 *signer, STACK_OF(X509) **chain);
 static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
                                   STACK_OF(X509) *chain);
-static int ts_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert);
-static int ts_issuer_serial_cmp(ESS_ISSUER_SERIAL *is, X509 *cert);
+
 static int int_ts_RESP_verify_token(TS_VERIFY_CTX *ctx,
                                     PKCS7 *token, TS_TST_INFO *tst_info);
 static int ts_check_status_info(TS_RESP *response);
@@ -44,7 +43,6 @@ static int ts_check_nonces(const ASN1_INTEGER *a, TS_TST_INFO *tst_info);
 static int ts_check_signer_name(GENERAL_NAME *tsa_name, X509 *signer);
 static int ts_find_name(STACK_OF(GENERAL_NAME) *gen_names,
                         GENERAL_NAME *name);
-static int ts_find_cert_v2(STACK_OF(ESS_CERT_ID_V2) *cert_ids, X509 *cert);
 
 /*
  * This must be large enough to hold all values in ts_status_text (with
@@ -218,7 +216,7 @@ static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
     if (ss != NULL) {
         cert_ids = ss->cert_ids;
         cert = sk_X509_value(chain, 0);
-        if (ts_find_cert(cert_ids, cert) != 0)
+        if (ess_find_cert(cert_ids, cert) != 0)
             goto err;
 
         /*
@@ -228,14 +226,14 @@ static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
         if (sk_ESS_CERT_ID_num(cert_ids) > 1) {
             for (i = 1; i < sk_X509_num(chain); ++i) {
                 cert = sk_X509_value(chain, i);
-                if (ts_find_cert(cert_ids, cert) < 0)
+                if (ess_find_cert(cert_ids, cert) < 0)
                     goto err;
             }
         }
     } else if (ssv2 != NULL) {
         cert_ids_v2 = ssv2->cert_ids;
         cert = sk_X509_value(chain, 0);
-        if (ts_find_cert_v2(cert_ids_v2, cert) != 0)
+        if (ess_find_cert_v2(cert_ids_v2, cert) != 0)
             goto err;
 
         /*
@@ -245,7 +243,7 @@ static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
         if (sk_ESS_CERT_ID_V2_num(cert_ids_v2) > 1) {
             for (i = 1; i < sk_X509_num(chain); ++i) {
                 cert = sk_X509_value(chain, i);
-                if (ts_find_cert_v2(cert_ids_v2, cert) < 0)
+                if (ess_find_cert_v2(cert_ids_v2, cert) < 0)
                     goto err;
             }
         }
@@ -263,87 +261,6 @@ static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
     return ret;
 }
 
-/* Returns < 0 if certificate is not found, certificate index otherwise. */
-static int ts_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert)
-{
-    int i;
-    unsigned char cert_sha1[SHA_DIGEST_LENGTH];
-
-    if (!cert_ids || !cert)
-        return -1;
-
-    /* Recompute SHA1 hash of certificate if necessary (side effect). */
-    X509_check_purpose(cert, -1, 0);
-
-    if (!X509_digest(cert, EVP_sha1(), cert_sha1, NULL))
-        return -1;
-
-    /* Look for cert in the cert_ids vector. */
-    for (i = 0; i < sk_ESS_CERT_ID_num(cert_ids); ++i) {
-        ESS_CERT_ID *cid = sk_ESS_CERT_ID_value(cert_ids, i);
-
-        if (cid->hash->length == SHA_DIGEST_LENGTH
-            && memcmp(cid->hash->data, cert_sha1, SHA_DIGEST_LENGTH) == 0) {
-            ESS_ISSUER_SERIAL *is = cid->issuer_serial;
-            if (!is || !ts_issuer_serial_cmp(is, cert))
-                return i;
-        }
-    }
-
-    return -1;
-}
-
-/* Returns < 0 if certificate is not found, certificate index otherwise. */
-static int ts_find_cert_v2(STACK_OF(ESS_CERT_ID_V2) *cert_ids, X509 *cert)
-{
-    int i;
-    unsigned char cert_digest[EVP_MAX_MD_SIZE];
-    unsigned int len;
-
-    /* Look for cert in the cert_ids vector. */
-    for (i = 0; i < sk_ESS_CERT_ID_V2_num(cert_ids); ++i) {
-        ESS_CERT_ID_V2 *cid = sk_ESS_CERT_ID_V2_value(cert_ids, i);
-        const EVP_MD *md;
-
-        if (cid->hash_alg != NULL)
-            md = EVP_get_digestbyobj(cid->hash_alg->algorithm);
-        else
-            md = EVP_sha256();
-
-        if (!X509_digest(cert, md, cert_digest, &len))
-            return -1;
-        if (cid->hash->length != (int)len)
-            return -1;
-
-        if (memcmp(cid->hash->data, cert_digest, cid->hash->length) == 0) {
-            ESS_ISSUER_SERIAL *is = cid->issuer_serial;
-
-            if (is == NULL || !ts_issuer_serial_cmp(is, cert))
-                return i;
-        }
-    }
-
-    return -1;
-}
-
-static int ts_issuer_serial_cmp(ESS_ISSUER_SERIAL *is, X509 *cert)
-{
-    GENERAL_NAME *issuer;
-
-    if (!is || !cert || sk_GENERAL_NAME_num(is->issuer) != 1)
-        return -1;
-
-    issuer = sk_GENERAL_NAME_value(is->issuer, 0);
-    if (issuer->type != GEN_DIRNAME
-        || X509_NAME_cmp(issuer->d.dirn, X509_get_issuer_name(cert)))
-        return -1;
-
-    if (ASN1_INTEGER_cmp(is->serial, X509_get_serialNumber(cert)))
-        return -1;
-
-    return 0;
-}
-
 /*-
  * Verifies whether 'response' contains a valid response with regards
  * to the settings of the context:
index 375d358703a39ce173ae5b206c749f17fd55293e..5ef1219a2ef46c1102a5da3abad3f7b8d101a441 100644 (file)
@@ -569,7 +569,8 @@ Message-digest of the eContent OCTET STRING within encapContentInfo being signed
 
 =item *
 
-An ESS signing-certificate or ESS signing-certificate-v2 attribute, as defined in Enhanced Security Services (ESS), RFC 2634 and RFC 5035.
+An ESS signing-certificate or ESS signing-certificate-v2 attribute, as defined 
+in Enhanced Security Services (ESS), RFC 2634 and RFC 5035.
 An ESS signing-certificate attribute only allows for the use of SHA-1 as a digest algorithm.
 An ESS signing-certificate-v2 attribute allows for the use of any digest algorithm.
 
@@ -577,9 +578,10 @@ An ESS signing-certificate-v2 attribute allows for the use of any digest algorit
 
 The digital signature value computed on the user data and, when present, on the signed attributes.
 
-Note that currently the B<-cades> option applies only to the B<-sign> operation and is ignored during
-the B<-verify> operation, i.e. the signing certification is not checked during the verification process.
-This feature might be added in a future version.
+NOTE that the B<-cades> option applies to the B<-sign> or B<-verify> operations.
+With this option, the B<-verify> operation also checks that the signing-certificates
+attribute is present, and its value matches the verification trust chain built
+during the verification process.
 
 =back
 
index 159c378b0e80e57f1d237f3a71cc13b662fb152f..ed289b1affaeafd0dc4015d8577100640e6a8387 100644 (file)
@@ -66,10 +66,14 @@ from the content. If the content is not of type B<text/plain> then an error is
 returned.
 
 If B<CMS_NO_SIGNER_CERT_VERIFY> is set the signing certificates are not
-verified.
+verified, unless CMS_CADES flag is also set.
 
 If B<CMS_NO_ATTR_VERIFY> is set the signed attributes signature is not
-verified.
+verified, unless CMS_CADES flag is also set.
+
+If B<CMS_CADES> is set, each signer certificate is checked against the 
+"ESS signing-certificate" extension added in the signed attributes of the 
+signature.
 
 If B<CMS_NO_CONTENT_VERIFY> is set then the content digest is not checked.
 
@@ -122,7 +126,7 @@ L<ERR_get_error(3)>, L<CMS_sign(3)>
 
 =head1 COPYRIGHT
 
-Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2008-2020 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 c630991d68a1a17bca090eaeea44670b07bfddd9..67263fa886463efa8abab889b960139734f9a19d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2020 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
@@ -7,7 +7,15 @@
  * https://www.openssl.org/source/license.html
  */
 
+#ifndef OPENSSL_NO_CMS
+
 /* internal CMS-ESS related stuff */
 
 int cms_add1_signing_cert(CMS_SignerInfo *si, ESS_SIGNING_CERT *sc);
 int cms_add1_signing_cert_v2(CMS_SignerInfo *si, ESS_SIGNING_CERT_V2 *sc);
+
+int cms_signerinfo_get_signing_cert_v2(CMS_SignerInfo *si,
+                                       ESS_SIGNING_CERT_V2 **psc);
+int cms_signerinfo_get_signing_cert(CMS_SignerInfo *si,
+                                    ESS_SIGNING_CERT **psc);
+#endif
index ac6c5c61d7afd10f6973e49d54f5953273aa1ab4..6ae9a8180bb741937c1bcc17d8881d898f255b05 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2020 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
@@ -24,6 +24,10 @@ ESS_SIGNING_CERT_V2 *ESS_SIGNING_CERT_V2_new_init(const EVP_MD *hash_alg,
                                                   STACK_OF(X509) *certs,
                                                   int issuer_needed);
 
+/* Returns < 0 if certificate is not found, certificate index otherwise. */
+int ess_find_cert_v2(const STACK_OF(ESS_CERT_ID_V2) *cert_ids, const X509 *cert);
+int ess_find_cert(const STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert);
+
 /*-
  * IssuerSerial ::= SEQUENCE {
  *        issuer                  GeneralNames,
index 6cff785caace3e16399efcf3b2ba31301016b567..97704bfa52719716e494383ec754ef708289b2d6 100644 (file)
@@ -109,6 +109,7 @@ int ERR_load_CMS_strings(void);
 #   define CMS_F_CMS_SIGN                                   0
 #   define CMS_F_CMS_SIGNED_DATA_INIT                       0
 #   define CMS_F_CMS_SIGNERINFO_CONTENT_SIGN                0
+#   define CMS_F_CMS_SIGNERINFO_GET_CHAIN                   0
 #   define CMS_F_CMS_SIGNERINFO_SIGN                        0
 #   define CMS_F_CMS_SIGNERINFO_VERIFY                      0
 #   define CMS_F_CMS_SIGNERINFO_VERIFY_CERT                 0
@@ -118,6 +119,7 @@ int ERR_load_CMS_strings(void);
 #   define CMS_F_CMS_STREAM                                 0
 #   define CMS_F_CMS_UNCOMPRESS                             0
 #   define CMS_F_CMS_VERIFY                                 0
+#   define CMS_F_ESS_CHECK_SIGNING_CERTS                    0
 #   define CMS_F_KEK_UNWRAP_KEY                             0
 # endif
 
@@ -147,6 +149,8 @@ int ERR_load_CMS_strings(void);
 #  define CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE      114
 #  define CMS_R_ERROR_SETTING_KEY                          115
 #  define CMS_R_ERROR_SETTING_RECIPIENTINFO                116
+#  define CMS_R_ESS_NO_SIGNING_CERTID_ATTRIBUTE            182
+#  define CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR          183
 #  define CMS_R_INVALID_ENCRYPTED_KEY_LENGTH               117
 #  define CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER           176
 #  define CMS_R_INVALID_KEY_LENGTH                         118
index 8befce5c4849a62549774bdfb099f7b7390e08f5..ff3c3d3d95cb22bee66cd4a2cb9e88ee7d5dec46 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2020 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
@@ -10,6 +10,7 @@
 
 #ifndef OPENSSL_ESSERR_H
 # define OPENSSL_ESSERR_H
+# pragma once
 
 # include <openssl/opensslconf.h>
 # include <openssl/symhacks.h>
index db2fce650d519e13f212a35099d9401b2c260190..76342481fb24d2024415c1977be26adefcc9bba6 100644 (file)
@@ -30,7 +30,7 @@ my $smcont   = srctop_file("test", "smcont.txt");
 my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
     = disabled qw/des dh dsa ec ec2m rc2 zlib/;
 
-plan tests => 7;
+plan tests => 10;
 
 my @smime_pkcs7_tests = (
 
@@ -251,26 +251,6 @@ my @smime_cms_tests = (
         "-CAfile", catfile($smdir, "smroot.pem") ]
     ],
 
-    [ "signed content DER format, RSA key, CAdES-BES compatible",
-      [ "{cmd1}", "-sign", "-cades", "-in", $smcont, "-outform", "DER",
-        "-nodetach",
-        "-certfile", catfile($smdir, "smroot.pem"),
-        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
-      [ "{cmd2}", "-verify", "-in", "{output}.cms", "-inform", "DER",
-        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
-      \&final_compare
-    ],
-
-    [ "signed content DER format, RSA key, SHA256 md, CAdES-BES compatible",
-      [ "{cmd1}", "-sign", "-cades", "-md", "sha256", "-in", $smcont,
-        "-outform", "DER", "-nodetach",
-        "-certfile", catfile($smdir, "smroot.pem"),
-        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
-      [ "{cmd2}", "-verify", "-in", "{output}.cms", "-inform", "DER",
-        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
-      \&final_compare
-    ],
-
     [ "enveloped content test streaming S/MIME format, DES, 3 recipients, keyid",
       [ "{cmd1}", "-encrypt", "-in", $smcont,
         "-stream", "-out", "{output}.cms", "-keyid",
@@ -360,6 +340,87 @@ my @smime_cms_tests = (
 
 );
 
+my @smime_cms_cades_tests = (
+
+    [ "signed content DER format, RSA key, CAdES-BES compatible",
+      [ "{cmd1}", "-sign", "-cades", "-in", $smcont, "-outform", "DER",
+         "-nodetach",
+        "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
+      [ "{cmd2}", "-verify", "-cades", "-in", "{output}.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ],
+
+    [ "signed content DER format, RSA key, SHA256 md, CAdES-BES compatible",
+      [ "{cmd1}", "-sign", "-cades", "-md", "sha256", "-in", $smcont, "-outform",
+        "DER", "-nodetach", "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
+      [ "{cmd2}", "-verify", "-cades", "-in", "{output}.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ],
+
+    [ "signed content DER format, RSA key, SHA512 md, CAdES-BES compatible",
+      [ "{cmd1}", "-sign", "-cades", "-md", "sha512", "-in", $smcont, "-outform",
+        "DER", "-nodetach", "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
+      [ "{cmd2}", "-verify", "-cades", "-in", "{output}.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ],
+
+    [ "signed content DER format, RSA key, SHA256 md, CAdES-BES compatible",
+      [ "{cmd1}", "-sign", "-cades", "-binary",  "-nodetach", "-nosmimecap", "-md", "sha256",
+        "-in", $smcont, "-outform", "DER", 
+        "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"),
+        "-outform", "DER", "-out", "{output}.cms"  ],
+      [ "{cmd2}", "-verify", "-cades", "-in", "{output}.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ],
+
+    [ "resigned content DER format, RSA key, SHA256 md, CAdES-BES compatible",
+      [ "{cmd1}", "-sign", "-cades", "-binary",  "-nodetach", "-nosmimecap", "-md", "sha256",
+        "-in", $smcont, "-outform", "DER", 
+        "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"),
+        "-outform", "DER", "-out", "{output}.cms"  ],
+      [ "{cmd1}", "-resign", "-cades", "-binary", "-nodetach", "-nosmimecap", "-md", "sha256",
+        "-inform", "DER", "-in", "{output}.cms",
+        "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa2.pem"),
+        "-outform", "DER", "-out", "{output}2.cms" ],
+
+      [ "{cmd2}", "-verify", "-cades", "-in", "{output}2.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ],
+);
+
+my @smime_cms_cades_ko_tests = (
+    [ "signed content DER format, RSA key, but verified as CAdES-BES compatible",
+      [ "-sign", "-in", $smcont, "-outform", "DER", "-nodetach",
+        "-certfile", catfile($smdir, "smroot.pem"),
+        "-signer", catfile($smdir, "smrsa1.pem"), "-out", "{output}.cms" ],
+      [ "-verify", "-cades", "-in", "{output}.cms", "-inform", "DER",
+        "-CAfile", catfile($smdir, "smroot.pem"), "-out", "{output}.txt" ],
+      \&final_compare
+    ]
+);
+
+# cades options test - check that some combinations are rejected
+my @smime_cms_cades_invalid_option_tests = (
+    [
+        [ "-cades", "-noattr" ],
+    ],[
+        [ "-verify", "-cades", "-noattr" ],
+    ],[
+        [ "-verify", "-cades", "-noverify" ],
+    ],
+);
+
 my @smime_cms_comp_tests = (
 
     [ "compressed content test streaming PEM format",
@@ -491,7 +552,7 @@ my @smime_cms_param_tests = (
         "-in", "{output}.cms", "-out", "{output}.txt" ],
       \&final_compare
     ]
-    );
+);
 
 my @contenttype_cms_test = (
     [ "signed content test - check that content type is added to additional signerinfo, RSA keys",
@@ -542,7 +603,7 @@ sub runner_loop {
                       $x;
                   } @$_;
 
-                  diag "CMD: openssl", join(" ", @cmd);
+                  diag "CMD: openssl ", join(" ", @cmd);
                   $ok &&= run(app(["openssl", @cmd]));
                   $opts{input} = $opts{output};
               }
@@ -654,6 +715,36 @@ subtest "CMS Decrypt message encrypted with OpenSSL 1.1.1\n" => sub {
     }
 };
 
+subtest "CAdES <=> CAdES consistency tests\n" => sub {
+    plan tests => (scalar @smime_cms_cades_tests);
+
+    runner_loop(prefix => 'cms-cades', cmd1 => 'cms', cmd2 => 'cms',
+                tests => [ @smime_cms_cades_tests ]);
+};
+
+subtest "CAdES; cms incompatible arguments tests\n" => sub {
+    plan tests => (scalar @smime_cms_cades_invalid_option_tests);
+
+    foreach (@smime_cms_cades_invalid_option_tests) {
+        ok(!run(app(["openssl", "cms", @{$$_[0]} ] )));
+    }
+};
+
+subtest "CAdES ko tests\n" => sub {
+    plan tests => (scalar @smime_cms_cades_ko_tests);
+
+    foreach (@smime_cms_cades_ko_tests) {
+      SKIP: {
+        my $skip_reason = check_availability($$_[0]);
+        skip $skip_reason, 1 if $skip_reason;
+
+        ok(run(app(["openssl", "cms", @{$$_[1]}]))
+            && !run(app(["openssl", "cms", @{$$_[2]}])),
+            $$_[0]);
+        }
+    }
+};
+
 sub check_availability {
     my $tnam = shift;