/* pk7_smime.c */ /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL * project. */ /* ==================================================================== * Copyright (c) 1999-2004 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ /* Simple PKCS#7 processing functions */ #include #include "cryptlib.h" #include #include PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, BIO *data, int flags) { PKCS7 *p7; PKCS7_SIGNER_INFO *si; BIO *p7bio; STACK_OF(X509_ALGOR) *smcap; int i; if(!X509_check_private_key(signcert, pkey)) { PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); return NULL; } if(!(p7 = PKCS7_new())) { PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); return NULL; } PKCS7_set_type(p7, NID_pkcs7_signed); PKCS7_content_new(p7, NID_pkcs7_data); if (!(si = PKCS7_add_signature(p7,signcert,pkey,EVP_sha1()))) { PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR); PKCS7_free(p7); return NULL; } if(!(flags & PKCS7_NOCERTS)) { PKCS7_add_certificate(p7, signcert); if(certs) for(i = 0; i < sk_X509_num(certs); i++) PKCS7_add_certificate(p7, sk_X509_value(certs, i)); } if(!(flags & PKCS7_NOATTR)) { PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)); /* Add SMIMECapabilities */ if(!(flags & PKCS7_NOSMIMECAP)) { if(!(smcap = sk_X509_ALGOR_new_null())) { PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); PKCS7_free(p7); return NULL; } #ifndef OPENSSL_NO_DES PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1); #endif #ifndef OPENSSL_NO_RC2 PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128); PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64); #endif #ifndef OPENSSL_NO_DES PKCS7_simple_smimecap (smcap, NID_des_cbc, -1); #endif #ifndef OPENSSL_NO_RC2 PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40); #endif PKCS7_add_attrib_smimecap (si, smcap); sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); } } if (flags & PKCS7_STREAM) return p7; if (!(p7bio = PKCS7_dataInit(p7, NULL))) { PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); PKCS7_free(p7); return NULL; } SMIME_crlf_copy(data, p7bio, flags); if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1); if (!PKCS7_dataFinal(p7,p7bio)) { PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN); PKCS7_free(p7); BIO_free_all(p7bio); return NULL; } BIO_free_all(p7bio); return p7; } int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, BIO *indata, BIO *out, int flags) { STACK_OF(X509) *signers; X509 *signer; STACK_OF(PKCS7_SIGNER_INFO) *sinfos; PKCS7_SIGNER_INFO *si; X509_STORE_CTX cert_ctx; char buf[4096]; int i, j=0, k, ret = 0; BIO *p7bio; BIO *tmpin, *tmpout; if(!p7) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_INVALID_NULL_POINTER); return 0; } if(!PKCS7_type_is_signed(p7)) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_WRONG_CONTENT_TYPE); return 0; } /* Check for no data and no content: no data to verify signature */ if(PKCS7_get_detached(p7) && !indata) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_CONTENT); return 0; } #if 0 /* NB: this test commented out because some versions of Netscape * illegally include zero length content when signing data. */ /* Check for data and content: two sets of data */ if(!PKCS7_get_detached(p7) && indata) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CONTENT_AND_DATA_PRESENT); return 0; } #endif sinfos = PKCS7_get_signer_info(p7); if(!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_SIGNATURES_ON_DATA); return 0; } signers = PKCS7_get0_signers(p7, certs, flags); if(!signers) return 0; /* Now verify the certificates */ if (!(flags & PKCS7_NOVERIFY)) for (k = 0; k < sk_X509_num(signers); k++) { signer = sk_X509_value (signers, k); if (!(flags & PKCS7_NOCHAIN)) { if(!X509_STORE_CTX_init(&cert_ctx, store, signer, p7->d.sign->cert)) { PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); sk_X509_free(signers); return 0; } X509_STORE_CTX_set_purpose(&cert_ctx, X509_PURPOSE_SMIME_SIGN); } else if(!X509_STORE_CTX_init (&cert_ctx, store, signer, NULL)) { PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); sk_X509_free(signers); return 0; } if (!(flags & PKCS7_NOCRL)) X509_STORE_CTX_set0_crls(&cert_ctx, p7->d.sign->crl); i = X509_verify_cert(&cert_ctx); if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx); X509_STORE_CTX_cleanup(&cert_ctx); if (i <= 0) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR); ERR_add_error_data(2, "Verify error:", X509_verify_cert_error_string(j)); sk_X509_free(signers); return 0; } /* Check for revocation status here */ } /* Performance optimization: if the content is a memory BIO then * store its contents in a temporary read only memory BIO. This * avoids potentially large numbers of slow copies of data which will * occur when reading from a read write memory BIO when signatures * are calculated. */ if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM)) { char *ptr; long len; len = BIO_get_mem_data(indata, &ptr); tmpin = BIO_new_mem_buf(ptr, len); if (tmpin == NULL) { PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); return 0; } } else tmpin = indata; p7bio=PKCS7_dataInit(p7,tmpin); if(flags & PKCS7_TEXT) { if(!(tmpout = BIO_new(BIO_s_mem()))) { PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); goto err; } } else tmpout = out; /* We now have to 'read' from p7bio to calculate digests etc. */ for (;;) { i=BIO_read(p7bio,buf,sizeof(buf)); if (i <= 0) break; if (tmpout) BIO_write(tmpout, buf, i); } if(flags & PKCS7_TEXT) { if(!SMIME_text(tmpout, out)) { PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SMIME_TEXT_ERROR); BIO_free(tmpout); goto err; } BIO_free(tmpout); } /* Now Verify All Signatures */ if (!(flags & PKCS7_NOSIGS)) for (i=0; iissuer_and_serial; signer = NULL; /* If any certificates passed they take priority */ if (certs) signer = X509_find_by_issuer_and_serial (certs, ias->issuer, ias->serial); if (!signer && !(flags & PKCS7_NOINTERN) && p7->d.sign->cert) signer = X509_find_by_issuer_and_serial (p7->d.sign->cert, ias->issuer, ias->serial); if (!signer) { PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND); sk_X509_free(signers); return 0; } sk_X509_push(signers, signer); } return signers; } /* Build a complete PKCS#7 enveloped data */ PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, int flags) { PKCS7 *p7; BIO *p7bio = NULL; int i; X509 *x509; if(!(p7 = PKCS7_new())) { PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); return NULL; } PKCS7_set_type(p7, NID_pkcs7_enveloped); if(!PKCS7_set_cipher(p7, cipher)) { PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_ERROR_SETTING_CIPHER); goto err; } for(i = 0; i < sk_X509_num(certs); i++) { x509 = sk_X509_value(certs, i); if(!PKCS7_add_recipient(p7, x509)) { PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_ERROR_ADDING_RECIPIENT); goto err; } } if(!(p7bio = PKCS7_dataInit(p7, NULL))) { PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); goto err; } SMIME_crlf_copy(in, p7bio, flags); BIO_flush(p7bio); if (!PKCS7_dataFinal(p7,p7bio)) { PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_PKCS7_DATAFINAL_ERROR); goto err; } BIO_free_all(p7bio); return p7; err: BIO_free(p7bio); PKCS7_free(p7); return NULL; } int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags) { BIO *tmpmem; int ret, i; char buf[4096]; if(!p7) { PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_INVALID_NULL_POINTER); return 0; } if(!PKCS7_type_is_enveloped(p7)) { PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_WRONG_CONTENT_TYPE); return 0; } if(!X509_check_private_key(cert, pkey)) { PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); return 0; } if(!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) { PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR); return 0; } if (flags & PKCS7_TEXT) { BIO *tmpbuf, *bread; /* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */ if(!(tmpbuf = BIO_new(BIO_f_buffer()))) { PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); return 0; } if(!(bread = BIO_push(tmpbuf, tmpmem))) { PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); return 0; } ret = SMIME_text(bread, data); BIO_free_all(bread); return ret; } else { for(;;) { i = BIO_read(tmpmem, buf, sizeof(buf)); if(i <= 0) break; BIO_write(data, buf, i); } BIO_free_all(tmpmem); return 1; } }