You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
16 KiB
595 lines
16 KiB
/* crypto/cms/cms_lib.c */ |
|
/* |
|
* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL |
|
* project. |
|
*/ |
|
/* ==================================================================== |
|
* Copyright (c) 2008 The OpenSSL Project. All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in |
|
* the documentation and/or other materials provided with the |
|
* distribution. |
|
* |
|
* 3. All advertising materials mentioning features or use of this |
|
* software must display the following acknowledgment: |
|
* "This product includes software developed by the OpenSSL Project |
|
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
|
* |
|
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
|
* endorse or promote products derived from this software without |
|
* prior written permission. For written permission, please contact |
|
* licensing@OpenSSL.org. |
|
* |
|
* 5. Products derived from this software may not be called "OpenSSL" |
|
* nor may "OpenSSL" appear in their names without prior written |
|
* permission of the OpenSSL Project. |
|
* |
|
* 6. Redistributions of any form whatsoever must retain the following |
|
* acknowledgment: |
|
* "This product includes software developed by the OpenSSL Project |
|
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
|
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
|
* OF THE POSSIBILITY OF SUCH DAMAGE. |
|
* ==================================================================== |
|
*/ |
|
|
|
#include <openssl/asn1t.h> |
|
#include <openssl/x509.h> |
|
#include <openssl/err.h> |
|
#include <openssl/pem.h> |
|
#include <openssl/bio.h> |
|
#include <openssl/asn1.h> |
|
#include "cms.h" |
|
#include "cms_lcl.h" |
|
|
|
IMPLEMENT_ASN1_FUNCTIONS(CMS_ContentInfo) |
|
IMPLEMENT_ASN1_PRINT_FUNCTION(CMS_ContentInfo) |
|
|
|
DECLARE_ASN1_ITEM(CMS_CertificateChoices) |
|
DECLARE_ASN1_ITEM(CMS_RevocationInfoChoice) |
|
DECLARE_STACK_OF(CMS_CertificateChoices) |
|
DECLARE_STACK_OF(CMS_RevocationInfoChoice) |
|
|
|
const ASN1_OBJECT *CMS_get0_type(CMS_ContentInfo *cms) |
|
{ |
|
return cms->contentType; |
|
} |
|
|
|
CMS_ContentInfo *cms_Data_create(void) |
|
{ |
|
CMS_ContentInfo *cms; |
|
cms = CMS_ContentInfo_new(); |
|
if (cms) { |
|
cms->contentType = OBJ_nid2obj(NID_pkcs7_data); |
|
/* Never detached */ |
|
CMS_set_detached(cms, 0); |
|
} |
|
return cms; |
|
} |
|
|
|
BIO *cms_content_bio(CMS_ContentInfo *cms) |
|
{ |
|
ASN1_OCTET_STRING **pos = CMS_get0_content(cms); |
|
if (!pos) |
|
return NULL; |
|
/* If content detached data goes nowhere: create NULL BIO */ |
|
if (!*pos) |
|
return BIO_new(BIO_s_null()); |
|
/* |
|
* If content not detached and created return memory BIO |
|
*/ |
|
if (!*pos || ((*pos)->flags == ASN1_STRING_FLAG_CONT)) |
|
return BIO_new(BIO_s_mem()); |
|
/* Else content was read in: return read only BIO for it */ |
|
return BIO_new_mem_buf((*pos)->data, (*pos)->length); |
|
} |
|
|
|
BIO *CMS_dataInit(CMS_ContentInfo *cms, BIO *icont) |
|
{ |
|
BIO *cmsbio, *cont; |
|
if (icont) |
|
cont = icont; |
|
else |
|
cont = cms_content_bio(cms); |
|
if (!cont) { |
|
CMSerr(CMS_F_CMS_DATAINIT, CMS_R_NO_CONTENT); |
|
return NULL; |
|
} |
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_data: |
|
return cont; |
|
|
|
case NID_pkcs7_signed: |
|
cmsbio = cms_SignedData_init_bio(cms); |
|
break; |
|
|
|
case NID_pkcs7_digest: |
|
cmsbio = cms_DigestedData_init_bio(cms); |
|
break; |
|
#ifdef ZLIB |
|
case NID_id_smime_ct_compressedData: |
|
cmsbio = cms_CompressedData_init_bio(cms); |
|
break; |
|
#endif |
|
|
|
case NID_pkcs7_encrypted: |
|
cmsbio = cms_EncryptedData_init_bio(cms); |
|
break; |
|
|
|
case NID_pkcs7_enveloped: |
|
cmsbio = cms_EnvelopedData_init_bio(cms); |
|
break; |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_DATAINIT, CMS_R_UNSUPPORTED_TYPE); |
|
return NULL; |
|
} |
|
|
|
if (cmsbio) |
|
return BIO_push(cmsbio, cont); |
|
|
|
if (!icont) |
|
BIO_free(cont); |
|
return NULL; |
|
|
|
} |
|
|
|
int CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio) |
|
{ |
|
ASN1_OCTET_STRING **pos = CMS_get0_content(cms); |
|
if (!pos) |
|
return 0; |
|
/* If ebmedded content find memory BIO and set content */ |
|
if (*pos && ((*pos)->flags & ASN1_STRING_FLAG_CONT)) { |
|
BIO *mbio; |
|
unsigned char *cont; |
|
long contlen; |
|
mbio = BIO_find_type(cmsbio, BIO_TYPE_MEM); |
|
if (!mbio) { |
|
CMSerr(CMS_F_CMS_DATAFINAL, CMS_R_CONTENT_NOT_FOUND); |
|
return 0; |
|
} |
|
contlen = BIO_get_mem_data(mbio, &cont); |
|
/* Set bio as read only so its content can't be clobbered */ |
|
BIO_set_flags(mbio, BIO_FLAGS_MEM_RDONLY); |
|
BIO_set_mem_eof_return(mbio, 0); |
|
ASN1_STRING_set0(*pos, cont, contlen); |
|
(*pos)->flags &= ~ASN1_STRING_FLAG_CONT; |
|
} |
|
|
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_data: |
|
case NID_pkcs7_enveloped: |
|
case NID_pkcs7_encrypted: |
|
case NID_id_smime_ct_compressedData: |
|
/* Nothing to do */ |
|
return 1; |
|
|
|
case NID_pkcs7_signed: |
|
return cms_SignedData_final(cms, cmsbio); |
|
|
|
case NID_pkcs7_digest: |
|
return cms_DigestedData_do_final(cms, cmsbio, 0); |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_DATAFINAL, CMS_R_UNSUPPORTED_TYPE); |
|
return 0; |
|
} |
|
} |
|
|
|
/* |
|
* Return an OCTET STRING pointer to content. This allows it to be accessed |
|
* or set later. |
|
*/ |
|
|
|
ASN1_OCTET_STRING **CMS_get0_content(CMS_ContentInfo *cms) |
|
{ |
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_data: |
|
return &cms->d.data; |
|
|
|
case NID_pkcs7_signed: |
|
return &cms->d.signedData->encapContentInfo->eContent; |
|
|
|
case NID_pkcs7_enveloped: |
|
return &cms->d.envelopedData->encryptedContentInfo->encryptedContent; |
|
|
|
case NID_pkcs7_digest: |
|
return &cms->d.digestedData->encapContentInfo->eContent; |
|
|
|
case NID_pkcs7_encrypted: |
|
return &cms->d.encryptedData->encryptedContentInfo->encryptedContent; |
|
|
|
case NID_id_smime_ct_authData: |
|
return &cms->d.authenticatedData->encapContentInfo->eContent; |
|
|
|
case NID_id_smime_ct_compressedData: |
|
return &cms->d.compressedData->encapContentInfo->eContent; |
|
|
|
default: |
|
if (cms->d.other->type == V_ASN1_OCTET_STRING) |
|
return &cms->d.other->value.octet_string; |
|
CMSerr(CMS_F_CMS_GET0_CONTENT, CMS_R_UNSUPPORTED_CONTENT_TYPE); |
|
return NULL; |
|
|
|
} |
|
} |
|
|
|
/* |
|
* Return an ASN1_OBJECT pointer to content type. This allows it to be |
|
* accessed or set later. |
|
*/ |
|
|
|
static ASN1_OBJECT **cms_get0_econtent_type(CMS_ContentInfo *cms) |
|
{ |
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_signed: |
|
return &cms->d.signedData->encapContentInfo->eContentType; |
|
|
|
case NID_pkcs7_enveloped: |
|
return &cms->d.envelopedData->encryptedContentInfo->contentType; |
|
|
|
case NID_pkcs7_digest: |
|
return &cms->d.digestedData->encapContentInfo->eContentType; |
|
|
|
case NID_pkcs7_encrypted: |
|
return &cms->d.encryptedData->encryptedContentInfo->contentType; |
|
|
|
case NID_id_smime_ct_authData: |
|
return &cms->d.authenticatedData->encapContentInfo->eContentType; |
|
|
|
case NID_id_smime_ct_compressedData: |
|
return &cms->d.compressedData->encapContentInfo->eContentType; |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_GET0_ECONTENT_TYPE, CMS_R_UNSUPPORTED_CONTENT_TYPE); |
|
return NULL; |
|
|
|
} |
|
} |
|
|
|
const ASN1_OBJECT *CMS_get0_eContentType(CMS_ContentInfo *cms) |
|
{ |
|
ASN1_OBJECT **petype; |
|
petype = cms_get0_econtent_type(cms); |
|
if (petype) |
|
return *petype; |
|
return NULL; |
|
} |
|
|
|
int CMS_set1_eContentType(CMS_ContentInfo *cms, const ASN1_OBJECT *oid) |
|
{ |
|
ASN1_OBJECT **petype, *etype; |
|
petype = cms_get0_econtent_type(cms); |
|
if (!petype) |
|
return 0; |
|
if (!oid) |
|
return 1; |
|
etype = OBJ_dup(oid); |
|
if (!etype) |
|
return 0; |
|
ASN1_OBJECT_free(*petype); |
|
*petype = etype; |
|
return 1; |
|
} |
|
|
|
int CMS_is_detached(CMS_ContentInfo *cms) |
|
{ |
|
ASN1_OCTET_STRING **pos; |
|
pos = CMS_get0_content(cms); |
|
if (!pos) |
|
return -1; |
|
if (*pos) |
|
return 0; |
|
return 1; |
|
} |
|
|
|
int CMS_set_detached(CMS_ContentInfo *cms, int detached) |
|
{ |
|
ASN1_OCTET_STRING **pos; |
|
pos = CMS_get0_content(cms); |
|
if (!pos) |
|
return 0; |
|
if (detached) { |
|
if (*pos) { |
|
ASN1_OCTET_STRING_free(*pos); |
|
*pos = NULL; |
|
} |
|
return 1; |
|
} |
|
if (!*pos) |
|
*pos = ASN1_OCTET_STRING_new(); |
|
if (*pos) { |
|
/* |
|
* NB: special flag to show content is created and not read in. |
|
*/ |
|
(*pos)->flags |= ASN1_STRING_FLAG_CONT; |
|
return 1; |
|
} |
|
CMSerr(CMS_F_CMS_SET_DETACHED, ERR_R_MALLOC_FAILURE); |
|
return 0; |
|
} |
|
|
|
/* Set up an X509_ALGOR DigestAlgorithmIdentifier from an EVP_MD */ |
|
|
|
void cms_DigestAlgorithm_set(X509_ALGOR *alg, const EVP_MD *md) |
|
{ |
|
int param_type; |
|
|
|
if (md->flags & EVP_MD_FLAG_DIGALGID_ABSENT) |
|
param_type = V_ASN1_UNDEF; |
|
else |
|
param_type = V_ASN1_NULL; |
|
|
|
X509_ALGOR_set0(alg, OBJ_nid2obj(EVP_MD_type(md)), param_type, NULL); |
|
|
|
} |
|
|
|
/* Create a digest BIO from an X509_ALGOR structure */ |
|
|
|
BIO *cms_DigestAlgorithm_init_bio(X509_ALGOR *digestAlgorithm) |
|
{ |
|
BIO *mdbio = NULL; |
|
ASN1_OBJECT *digestoid; |
|
const EVP_MD *digest; |
|
X509_ALGOR_get0(&digestoid, NULL, NULL, digestAlgorithm); |
|
digest = EVP_get_digestbyobj(digestoid); |
|
if (!digest) { |
|
CMSerr(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO, |
|
CMS_R_UNKNOWN_DIGEST_ALGORIHM); |
|
goto err; |
|
} |
|
mdbio = BIO_new(BIO_f_md()); |
|
if (!mdbio || !BIO_set_md(mdbio, digest)) { |
|
CMSerr(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO, CMS_R_MD_BIO_INIT_ERROR); |
|
goto err; |
|
} |
|
return mdbio; |
|
err: |
|
if (mdbio) |
|
BIO_free(mdbio); |
|
return NULL; |
|
} |
|
|
|
/* Locate a message digest content from a BIO chain based on SignerInfo */ |
|
|
|
int cms_DigestAlgorithm_find_ctx(EVP_MD_CTX *mctx, BIO *chain, |
|
X509_ALGOR *mdalg) |
|
{ |
|
int nid; |
|
ASN1_OBJECT *mdoid; |
|
X509_ALGOR_get0(&mdoid, NULL, NULL, mdalg); |
|
nid = OBJ_obj2nid(mdoid); |
|
/* Look for digest type to match signature */ |
|
for (;;) { |
|
EVP_MD_CTX *mtmp; |
|
chain = BIO_find_type(chain, BIO_TYPE_MD); |
|
if (chain == NULL) { |
|
CMSerr(CMS_F_CMS_DIGESTALGORITHM_FIND_CTX, |
|
CMS_R_NO_MATCHING_DIGEST); |
|
return 0; |
|
} |
|
BIO_get_md_ctx(chain, &mtmp); |
|
if (EVP_MD_CTX_type(mtmp) == nid |
|
/* |
|
* Workaround for broken implementations that use signature |
|
* algorithm OID instead of digest. |
|
*/ |
|
|| EVP_MD_pkey_type(EVP_MD_CTX_md(mtmp)) == nid) |
|
return EVP_MD_CTX_copy_ex(mctx, mtmp); |
|
chain = BIO_next(chain); |
|
} |
|
} |
|
|
|
static STACK_OF(CMS_CertificateChoices) |
|
**cms_get0_certificate_choices(CMS_ContentInfo *cms) |
|
{ |
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_signed: |
|
return &cms->d.signedData->certificates; |
|
|
|
case NID_pkcs7_enveloped: |
|
return &cms->d.envelopedData->originatorInfo->certificates; |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_GET0_CERTIFICATE_CHOICES, |
|
CMS_R_UNSUPPORTED_CONTENT_TYPE); |
|
return NULL; |
|
|
|
} |
|
} |
|
|
|
CMS_CertificateChoices *CMS_add0_CertificateChoices(CMS_ContentInfo *cms) |
|
{ |
|
STACK_OF(CMS_CertificateChoices) **pcerts; |
|
CMS_CertificateChoices *cch; |
|
pcerts = cms_get0_certificate_choices(cms); |
|
if (!pcerts) |
|
return NULL; |
|
if (!*pcerts) |
|
*pcerts = sk_CMS_CertificateChoices_new_null(); |
|
if (!*pcerts) |
|
return NULL; |
|
cch = M_ASN1_new_of(CMS_CertificateChoices); |
|
if (!cch) |
|
return NULL; |
|
if (!sk_CMS_CertificateChoices_push(*pcerts, cch)) { |
|
M_ASN1_free_of(cch, CMS_CertificateChoices); |
|
return NULL; |
|
} |
|
return cch; |
|
} |
|
|
|
int CMS_add0_cert(CMS_ContentInfo *cms, X509 *cert) |
|
{ |
|
CMS_CertificateChoices *cch; |
|
STACK_OF(CMS_CertificateChoices) **pcerts; |
|
int i; |
|
pcerts = cms_get0_certificate_choices(cms); |
|
if (!pcerts) |
|
return 0; |
|
for (i = 0; i < sk_CMS_CertificateChoices_num(*pcerts); i++) { |
|
cch = sk_CMS_CertificateChoices_value(*pcerts, i); |
|
if (cch->type == CMS_CERTCHOICE_CERT) { |
|
if (!X509_cmp(cch->d.certificate, cert)) { |
|
CMSerr(CMS_F_CMS_ADD0_CERT, |
|
CMS_R_CERTIFICATE_ALREADY_PRESENT); |
|
return 0; |
|
} |
|
} |
|
} |
|
cch = CMS_add0_CertificateChoices(cms); |
|
if (!cch) |
|
return 0; |
|
cch->type = CMS_CERTCHOICE_CERT; |
|
cch->d.certificate = cert; |
|
return 1; |
|
} |
|
|
|
int CMS_add1_cert(CMS_ContentInfo *cms, X509 *cert) |
|
{ |
|
int r; |
|
r = CMS_add0_cert(cms, cert); |
|
if (r > 0) |
|
CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); |
|
return r; |
|
} |
|
|
|
static STACK_OF(CMS_RevocationInfoChoice) |
|
**cms_get0_revocation_choices(CMS_ContentInfo *cms) |
|
{ |
|
switch (OBJ_obj2nid(cms->contentType)) { |
|
|
|
case NID_pkcs7_signed: |
|
return &cms->d.signedData->crls; |
|
|
|
case NID_pkcs7_enveloped: |
|
return &cms->d.envelopedData->originatorInfo->crls; |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_GET0_REVOCATION_CHOICES, |
|
CMS_R_UNSUPPORTED_CONTENT_TYPE); |
|
return NULL; |
|
|
|
} |
|
} |
|
|
|
CMS_RevocationInfoChoice *CMS_add0_RevocationInfoChoice(CMS_ContentInfo *cms) |
|
{ |
|
STACK_OF(CMS_RevocationInfoChoice) **pcrls; |
|
CMS_RevocationInfoChoice *rch; |
|
pcrls = cms_get0_revocation_choices(cms); |
|
if (!pcrls) |
|
return NULL; |
|
if (!*pcrls) |
|
*pcrls = sk_CMS_RevocationInfoChoice_new_null(); |
|
if (!*pcrls) |
|
return NULL; |
|
rch = M_ASN1_new_of(CMS_RevocationInfoChoice); |
|
if (!rch) |
|
return NULL; |
|
if (!sk_CMS_RevocationInfoChoice_push(*pcrls, rch)) { |
|
M_ASN1_free_of(rch, CMS_RevocationInfoChoice); |
|
return NULL; |
|
} |
|
return rch; |
|
} |
|
|
|
int CMS_add0_crl(CMS_ContentInfo *cms, X509_CRL *crl) |
|
{ |
|
CMS_RevocationInfoChoice *rch; |
|
rch = CMS_add0_RevocationInfoChoice(cms); |
|
if (!rch) |
|
return 0; |
|
rch->type = CMS_REVCHOICE_CRL; |
|
rch->d.crl = crl; |
|
return 1; |
|
} |
|
|
|
int CMS_add1_crl(CMS_ContentInfo *cms, X509_CRL *crl) |
|
{ |
|
int r; |
|
r = CMS_add0_crl(cms, crl); |
|
if (r > 0) |
|
CRYPTO_add(&crl->references, 1, CRYPTO_LOCK_X509_CRL); |
|
return r; |
|
} |
|
|
|
STACK_OF(X509) *CMS_get1_certs(CMS_ContentInfo *cms) |
|
{ |
|
STACK_OF(X509) *certs = NULL; |
|
CMS_CertificateChoices *cch; |
|
STACK_OF(CMS_CertificateChoices) **pcerts; |
|
int i; |
|
pcerts = cms_get0_certificate_choices(cms); |
|
if (!pcerts) |
|
return NULL; |
|
for (i = 0; i < sk_CMS_CertificateChoices_num(*pcerts); i++) { |
|
cch = sk_CMS_CertificateChoices_value(*pcerts, i); |
|
if (cch->type == 0) { |
|
if (!certs) { |
|
certs = sk_X509_new_null(); |
|
if (!certs) |
|
return NULL; |
|
} |
|
if (!sk_X509_push(certs, cch->d.certificate)) { |
|
sk_X509_pop_free(certs, X509_free); |
|
return NULL; |
|
} |
|
CRYPTO_add(&cch->d.certificate->references, 1, CRYPTO_LOCK_X509); |
|
} |
|
} |
|
return certs; |
|
|
|
} |
|
|
|
STACK_OF(X509_CRL) *CMS_get1_crls(CMS_ContentInfo *cms) |
|
{ |
|
STACK_OF(X509_CRL) *crls = NULL; |
|
STACK_OF(CMS_RevocationInfoChoice) **pcrls; |
|
CMS_RevocationInfoChoice *rch; |
|
int i; |
|
pcrls = cms_get0_revocation_choices(cms); |
|
if (!pcrls) |
|
return NULL; |
|
for (i = 0; i < sk_CMS_RevocationInfoChoice_num(*pcrls); i++) { |
|
rch = sk_CMS_RevocationInfoChoice_value(*pcrls, i); |
|
if (rch->type == 0) { |
|
if (!crls) { |
|
crls = sk_X509_CRL_new_null(); |
|
if (!crls) |
|
return NULL; |
|
} |
|
if (!sk_X509_CRL_push(crls, rch->d.crl)) { |
|
sk_X509_CRL_pop_free(crls, X509_CRL_free); |
|
return NULL; |
|
} |
|
CRYPTO_add(&rch->d.crl->references, 1, CRYPTO_LOCK_X509_CRL); |
|
} |
|
} |
|
return crls; |
|
}
|
|
|