diff options
author | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2022-06-27 17:05:21 +0200 |
---|---|---|
committer | Dr. David von Oheimb <dev@ddvo.net> | 2022-07-16 08:15:49 +0200 |
commit | d7d3dae694fa4611c1cd953dccf81b3d2b4121c6 (patch) | |
tree | d4210058c101d76fc666b53c183fbd3e5fa6cff5 | |
parent | 4329a321c9f939f06e7705fa25ac55af3160acf6 (diff) | |
download | openssl-new-d7d3dae694fa4611c1cd953dccf81b3d2b4121c6.tar.gz |
CMS: add CMS_SignedData_verify(), a variant of CMS_verify() with extensions
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/18667)
-rw-r--r-- | crypto/cms/cms_asn1.c | 1 | ||||
-rw-r--r-- | crypto/cms/cms_env.c | 2 | ||||
-rw-r--r-- | crypto/cms/cms_local.h | 1 | ||||
-rw-r--r-- | crypto/cms/cms_sd.c | 41 | ||||
-rw-r--r-- | doc/man3/CMS_verify.pod | 84 | ||||
-rw-r--r-- | doc/man3/X509_dup.pod | 2 | ||||
-rw-r--r-- | include/openssl/cms.h.in | 7 | ||||
-rw-r--r-- | test/cmsapitest.c | 13 | ||||
-rw-r--r-- | util/libcrypto.num | 3 |
9 files changed, 119 insertions, 35 deletions
diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index 72cd14317d..785743163c 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -83,6 +83,7 @@ ASN1_NDEF_SEQUENCE(CMS_SignedData) = { ASN1_IMP_SET_OF_OPT(CMS_SignedData, crls, CMS_RevocationInfoChoice, 1), ASN1_SET_OF(CMS_SignedData, signerInfos, CMS_SignerInfo) } ASN1_NDEF_SEQUENCE_END(CMS_SignedData) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(CMS_SignedData) ASN1_SEQUENCE(CMS_OriginatorInfo) = { ASN1_IMP_SET_OF_OPT(CMS_OriginatorInfo, certificates, CMS_CertificateChoices, 0), diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index 471676d2f5..4648cd1372 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -270,7 +270,7 @@ BIO *CMS_EnvelopedData_decrypt(CMS_EnvelopedData *env, BIO *detached_data, end: if (ci != NULL) - ci->d.envelopedData = NULL; + ci->d.envelopedData = NULL; /* do not indirectly free |env| */ CMS_ContentInfo_free(ci); if (!res) { BIO_free(bio); diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h index d16ca95176..514a345d6e 100644 --- a/crypto/cms/cms_local.h +++ b/crypto/cms/cms_local.h @@ -21,7 +21,6 @@ typedef struct CMS_IssuerAndSerialNumber_st CMS_IssuerAndSerialNumber; typedef struct CMS_EncapsulatedContentInfo_st CMS_EncapsulatedContentInfo; typedef struct CMS_SignerIdentifier_st CMS_SignerIdentifier; -typedef struct CMS_SignedData_st CMS_SignedData; typedef struct CMS_OtherRevocationInfoFormat_st CMS_OtherRevocationInfoFormat; typedef struct CMS_OriginatorInfo_st CMS_OriginatorInfo; typedef struct CMS_EncryptedContentInfo_st CMS_EncryptedContentInfo; diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c index 9877ac48a0..07cb8b51c6 100644 --- a/crypto/cms/cms_sd.c +++ b/crypto/cms/cms_sd.c @@ -1048,6 +1048,47 @@ int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain) } +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, + unsigned int flags, + OSSL_LIB_CTX *libctx, const char *propq) +{ + CMS_ContentInfo *ci; + BIO *bio = NULL; + int i, res = 0; + + if (sd == NULL) { + ERR_raise(ERR_LIB_CMS, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if ((ci = CMS_ContentInfo_new_ex(libctx, propq)) == NULL) + return NULL; + if ((bio = BIO_new(BIO_s_mem())) == NULL) + goto end; + ci->contentType = OBJ_nid2obj(NID_pkcs7_signed); + ci->d.signedData = sd; + + for (i = 0; i < sk_X509_num(extra); i++) + if (!CMS_add1_cert(ci, sk_X509_value(extra, i))) + goto end; + for (i = 0; i < sk_X509_CRL_num(crls); i++) + if (!CMS_add1_crl(ci, sk_X509_CRL_value(crls, i))) + goto end; + res = CMS_verify(ci, scerts, store, detached_data, bio, flags); + + end: + if (ci != NULL) + ci->d.signedData = NULL; /* do not indirectly free |sd| */ + CMS_ContentInfo_free(ci); + if (!res) { + BIO_free(bio); + bio = NULL; + } + return bio; +} + int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs) { unsigned char *smder = NULL; diff --git a/doc/man3/CMS_verify.pod b/doc/man3/CMS_verify.pod index 6c9595e51e..fa7e44394d 100644 --- a/doc/man3/CMS_verify.pod +++ b/doc/man3/CMS_verify.pod @@ -2,67 +2,86 @@ =head1 NAME -CMS_verify, CMS_get0_signers - verify a CMS SignedData structure +CMS_verify, CMS_SignedData_verify, +CMS_get0_signers - verify a CMS SignedData structure =head1 SYNOPSIS #include <openssl/cms.h> int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, X509_STORE *store, - BIO *indata, BIO *out, unsigned int flags); + BIO *detached_data, BIO *out, unsigned int flags); + 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, + unsigned int flags, + OSSL_LIB_CTX *libctx, const char *propq); STACK_OF(X509) *CMS_get0_signers(CMS_ContentInfo *cms); =head1 DESCRIPTION -CMS_verify() verifies a CMS SignedData structure. B<cms> is the CMS_ContentInfo -structure to verify. B<certs> is a set of certificates in which to search for -the signing certificate(s). B<store> is a trusted certificate store used for -chain verification. B<indata> is the detached content if the content is not -present in B<cms>. The content is written to B<out> if it is not NULL. - -B<flags> is an optional set of flags, which can be used to modify the verify +CMS_verify() verifies a B<CMS SignedData> structure +contained in a structure of type B<CMS_ContentInfo>. +I<cms> points to the B<CMS_ContentInfo> structure to verify. +I<certs> is a set of certificates in which to search for signing certificate(s). +I<store> is a trusted certificate store used for chain verification. +I<detached_data> refers to the content if the content is not present in I<cms>. +The content is written to the BIO I<out> if it is not NULL. +I<flags> is an optional set of flags, which can be used to modify the verify operation. -CMS_get0_signers() retrieves the signing certificate(s) from B<cms>, it may only -be called after a successful CMS_verify() operation. +CMS_SignedData_verify() is like CMS_verify() except that +it operates on B<CMS SignedData> input in the I<sd> argument, +it has some additional parameters described next, +and on success it returns the verfied content as a memory BIO. +The optional I<extra> parameter may be used to provide untrusted CA +certificates that may be helpful for chain building in certificate valiation. +This list of certificates must not contain duplicates. +The optional I<crls> parameter may be used to provide extra CRLs. +Also the list of CRLs must not contain duplicates. +The optional parameters library context I<libctx> and property query I<propq> +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. =head1 VERIFY PROCESS Normally the verify process proceeds as follows. -Initially some sanity checks are performed on B<cms>. The type of B<cms> must +Initially some sanity checks are performed on I<cms>. The type of I<cms> must be SignedData. There must be at least one signature on the data and if -the content is detached B<indata> cannot be B<NULL>. +the content is detached I<detached_data> cannot be NULL. An attempt is made to locate all the signing certificate(s), first looking in -the B<certs> parameter (if it is not NULL) and then looking in any -certificates contained in the B<cms> structure itself. If any signing +the I<certs> parameter (if it is not NULL) and then looking in any +certificates contained in the I<cms> structure itself. If any signing certificate cannot be located the operation fails. -Each signing certificate is chain verified using the B<smimesign> purpose and +Each signing certificate is chain verified using the I<smimesign> purpose and the supplied trusted certificate store. Any internal certificates in the message -are used as untrusted CAs. If CRL checking is enabled in B<store> any internal -CRLs are used in addition to attempting to look them up in B<store>. If any +are used as untrusted CAs. If CRL checking is enabled in I<store> any internal +CRLs are used in addition to attempting to look them up in I<store>. If any chain verify fails an error code is returned. -Finally the signed content is read (and written to B<out> if it is not NULL) +Finally the signed content is read (and written to I<out> if it is not NULL) and the signature's checked. If all signature's verify correctly then the function is successful. -Any of the following flags (ored together) can be passed in the B<flags> +Any of the following flags (ored together) can be passed in the I<flags> parameter to change the default verify behaviour. If B<CMS_NOINTERN> is set the certificates in the message itself are not searched when locating the signing certificate(s). This means that all the -signing certificates must be in the B<certs> parameter. +signing certificates must be in the I<certs> parameter. -If B<CMS_NOCRL> is set and CRL checking is enabled in B<store> then any -CRLs in the message itself are ignored. +If B<CMS_NOCRL> is set and CRL checking is enabled in I<store> then any +CRLs in the message itself and provided via the I<crls> parameter are ignored. -If the B<CMS_TEXT> flag is set MIME headers for type B<text/plain> are deleted -from the content. If the content is not of type B<text/plain> then an error is +If the B<CMS_TEXT> flag is set MIME headers for type C<text/plain> are deleted +from the content. If the content is not of type C<text/plain> then an error is returned. If B<CMS_NO_SIGNER_CERT_VERIFY> is set the signing certificates are not @@ -81,8 +100,8 @@ If B<CMS_NO_CONTENT_VERIFY> is set then the content digest is not checked. One application of B<CMS_NOINTERN> is to only accept messages signed by a small number of certificates. The acceptable certificates would be passed -in the B<certs> parameter. In this case if the signer is not one of the -certificates supplied in B<certs> then the verify will fail because the +in the I<certs> parameter. In this case if the signer is not one of the +certificates supplied in I<certs> then the verify will fail because the signer cannot be found. In some cases the standard techniques for looking up and validating @@ -94,7 +113,7 @@ using the signed data utility functions. Care should be taken when modifying the default verify behaviour, for example setting B<CMS_NO_CONTENT_VERIFY> will totally disable all content verification and any modified content will be considered valid. This combination is however -useful if one merely wishes to write the content to B<out> and its validity +useful if one merely wishes to write the content to I<out> and its validity is not considered important. Chain verification should arguably be performed using the signing time rather @@ -107,9 +126,12 @@ timestamp). CMS_verify() returns 1 for a successful verification and zero if an error occurred. +CMS_SignedData_verify() returns a memory BIO containing the verfied content, +or NULL on error. + CMS_get0_signers() returns all signers or NULL if an error occurred. -The error can be obtained from L<ERR_get_error(3)> +The error can be obtained from L<ERR_get_error(3)>. =head1 BUGS @@ -125,6 +147,10 @@ be held in memory if it is not detached. L<OSSL_ESS_check_signing_certs(3)>, L<ERR_get_error(3)>, L<CMS_sign(3)> +=head1 HISTORY + +CMS_SignedData_verify() was added in OpenSSL 3.1. + =head1 COPYRIGHT Copyright 2008-2021 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod index e24922bcf1..9e70ffea13 100644 --- a/doc/man3/X509_dup.pod +++ b/doc/man3/X509_dup.pod @@ -34,6 +34,8 @@ CMS_ContentInfo_print_ctx, CMS_EnvelopedData_it, CMS_ReceiptRequest_free, CMS_ReceiptRequest_new, +CMS_SignedData_free, +CMS_SignedData_new, CRL_DIST_POINTS_free, CRL_DIST_POINTS_new, DIRECTORYSTRING_free, diff --git a/include/openssl/cms.h.in b/include/openssl/cms.h.in index 4be44b4bac..239667700a 100644 --- a/include/openssl/cms.h.in +++ b/include/openssl/cms.h.in @@ -35,6 +35,7 @@ extern "C" { typedef struct CMS_EnvelopedData_st CMS_EnvelopedData; typedef struct CMS_ContentInfo_st CMS_ContentInfo; typedef struct CMS_SignerInfo_st CMS_SignerInfo; +typedef struct CMS_SignedData_st CMS_SignedData; typedef struct CMS_CertificateChoices CMS_CertificateChoices; typedef struct CMS_RevocationInfoChoice_st CMS_RevocationInfoChoice; typedef struct CMS_RecipientInfo_st CMS_RecipientInfo; @@ -51,6 +52,7 @@ typedef struct CMS_OtherKeyAttribute_st CMS_OtherKeyAttribute; -} DECLARE_ASN1_ITEM(CMS_EnvelopedData) +DECLARE_ASN1_ALLOC_FUNCTIONS(CMS_SignedData) DECLARE_ASN1_FUNCTIONS(CMS_ContentInfo) DECLARE_ASN1_FUNCTIONS(CMS_ReceiptRequest) DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo) @@ -295,6 +297,11 @@ 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); +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, + unsigned int flags, + OSSL_LIB_CTX *libctx, const char *propq); int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs); int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs, diff --git a/test/cmsapitest.c b/test/cmsapitest.c index b40089becd..460e4693c2 100644 --- a/test/cmsapitest.c +++ b/test/cmsapitest.c @@ -13,6 +13,7 @@ #include <openssl/bio.h> #include <openssl/x509.h> #include <openssl/pem.h> +#include "../crypto/cms/cms_local.h" /* for access to cms->d.signedData */ #include "testutil.h" @@ -81,8 +82,9 @@ static int test_encrypt_decrypt_aes_256_gcm(void) static int test_d2i_CMS_bio_NULL(void) { - BIO *bio; + BIO *bio, *content = NULL; CMS_ContentInfo *cms = NULL; + unsigned int flags = CMS_NO_SIGNER_CERT_VERIFY; int ret = 0; /* @@ -281,9 +283,12 @@ static int test_d2i_CMS_bio_NULL(void) }; ret = TEST_ptr(bio = BIO_new_mem_buf(cms_data, sizeof(cms_data))) - && TEST_ptr(cms = d2i_CMS_bio(bio, NULL)) - && TEST_true(CMS_verify(cms, NULL, NULL, NULL, NULL, - CMS_NO_SIGNER_CERT_VERIFY)); + && TEST_ptr(cms = d2i_CMS_bio(bio, NULL)) + && TEST_true(CMS_verify(cms, NULL, NULL, NULL, NULL, flags)) + && TEST_ptr(content = + CMS_SignedData_verify(cms->d.signedData, NULL, NULL, NULL, + NULL, NULL, flags, NULL, NULL)); + BIO_free(content); CMS_ContentInfo_free(cms); BIO_free(bio); return ret; diff --git a/util/libcrypto.num b/util/libcrypto.num index cb0d48cfca..3922eb30ad 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5440,5 +5440,8 @@ BIO_ADDR_dup ? 3_1_0 EXIST::FUNCTION:SOCK CMS_final_digest ? 3_1_0 EXIST::FUNCTION:CMS CMS_EnvelopedData_it ? 3_1_0 EXIST::FUNCTION:CMS CMS_EnvelopedData_decrypt ? 3_1_0 EXIST::FUNCTION:CMS +CMS_SignedData_free ? 3_1_0 EXIST::FUNCTION:CMS +CMS_SignedData_new ? 3_1_0 EXIST::FUNCTION:CMS +CMS_SignedData_verify ? 3_1_0 EXIST::FUNCTION:CMS OPENSSL_strcasecmp ? 3_0_3 EXIST::FUNCTION: OPENSSL_strncasecmp ? 3_0_3 EXIST::FUNCTION: |