sl@0: /* pk7_smime.c */ sl@0: /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL sl@0: * project. sl@0: */ sl@0: /* ==================================================================== sl@0: * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * licensing@OpenSSL.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: /* sl@0: © Portions copyright (c) 2006 Nokia Corporation. All rights reserved. sl@0: */ sl@0: /* Simple PKCS#7 processing functions */ sl@0: sl@0: #include sl@0: #include "cryptlib.h" sl@0: #include sl@0: #include sl@0: sl@0: EXPORT_C PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, sl@0: BIO *data, int flags) sl@0: { sl@0: PKCS7 *p7 = NULL; sl@0: PKCS7_SIGNER_INFO *si; sl@0: BIO *p7bio = NULL; sl@0: STACK_OF(X509_ALGOR) *smcap = NULL; sl@0: int i; sl@0: sl@0: if(!X509_check_private_key(signcert, pkey)) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); sl@0: return NULL; sl@0: } sl@0: sl@0: if(!(p7 = PKCS7_new())) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); sl@0: return NULL; sl@0: } sl@0: sl@0: if (!PKCS7_set_type(p7, NID_pkcs7_signed)) sl@0: goto err; sl@0: sl@0: if (!PKCS7_content_new(p7, NID_pkcs7_data)) sl@0: goto err; sl@0: sl@0: if (!(si = PKCS7_add_signature(p7,signcert,pkey,EVP_sha1()))) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR); sl@0: goto err; sl@0: } sl@0: sl@0: if(!(flags & PKCS7_NOCERTS)) { sl@0: if (!PKCS7_add_certificate(p7, signcert)) sl@0: goto err; sl@0: if(certs) for(i = 0; i < sk_X509_num(certs); i++) sl@0: if (!PKCS7_add_certificate(p7, sk_X509_value(certs, i))) sl@0: goto err; sl@0: } sl@0: sl@0: if(!(flags & PKCS7_NOATTR)) { sl@0: if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, sl@0: V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data))) sl@0: goto err; sl@0: /* Add SMIMECapabilities */ sl@0: if(!(flags & PKCS7_NOSMIMECAP)) sl@0: { sl@0: if(!(smcap = sk_X509_ALGOR_new_null())) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); sl@0: goto err; sl@0: } sl@0: #ifndef OPENSSL_NO_DES sl@0: if (!PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1)) sl@0: goto err; sl@0: #endif sl@0: #ifndef OPENSSL_NO_RC2 sl@0: if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128)) sl@0: goto err; sl@0: if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64)) sl@0: goto err; sl@0: #endif sl@0: #ifndef OPENSSL_NO_DES sl@0: if (!PKCS7_simple_smimecap (smcap, NID_des_cbc, -1)) sl@0: goto err; sl@0: #endif sl@0: #ifndef OPENSSL_NO_RC2 sl@0: if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40)) sl@0: goto err; sl@0: #endif sl@0: if (!PKCS7_add_attrib_smimecap (si, smcap)) sl@0: goto err; sl@0: sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); sl@0: smcap = NULL; sl@0: } sl@0: } sl@0: if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1); sl@0: sl@0: if (flags & PKCS7_STREAM) sl@0: return p7; sl@0: sl@0: if (!(p7bio = PKCS7_dataInit(p7, NULL))) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); sl@0: goto err; sl@0: } sl@0: sl@0: SMIME_crlf_copy(data, p7bio, flags); sl@0: sl@0: if (!PKCS7_dataFinal(p7,p7bio)) { sl@0: PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN); sl@0: goto err; sl@0: } sl@0: sl@0: BIO_free_all(p7bio); sl@0: return p7; sl@0: err: sl@0: sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); sl@0: BIO_free_all(p7bio); sl@0: PKCS7_free(p7); sl@0: return NULL; sl@0: } sl@0: sl@0: EXPORT_C int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, sl@0: BIO *indata, BIO *out, int flags) sl@0: { sl@0: STACK_OF(X509) *signers; sl@0: X509 *signer; sl@0: STACK_OF(PKCS7_SIGNER_INFO) *sinfos; sl@0: PKCS7_SIGNER_INFO *si; sl@0: X509_STORE_CTX cert_ctx; sl@0: #ifndef SYMBIAN sl@0: char buf[4096]; sl@0: #else sl@0: char buf[512]; sl@0: #endif sl@0: sl@0: int i, j=0, k, ret = 0; sl@0: BIO *p7bio; sl@0: BIO *tmpin, *tmpout; sl@0: sl@0: if(!p7) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_INVALID_NULL_POINTER); sl@0: return 0; sl@0: } sl@0: sl@0: if(!PKCS7_type_is_signed(p7)) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_WRONG_CONTENT_TYPE); sl@0: return 0; sl@0: } sl@0: sl@0: /* Check for no data and no content: no data to verify signature */ sl@0: if(PKCS7_get_detached(p7) && !indata) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_CONTENT); sl@0: return 0; sl@0: } sl@0: #if 0 sl@0: /* NB: this test commented out because some versions of Netscape sl@0: * illegally include zero length content when signing data. sl@0: */ sl@0: sl@0: /* Check for data and content: two sets of data */ sl@0: if(!PKCS7_get_detached(p7) && indata) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CONTENT_AND_DATA_PRESENT); sl@0: return 0; sl@0: } sl@0: #endif sl@0: sl@0: sinfos = PKCS7_get_signer_info(p7); sl@0: sl@0: if(!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_SIGNATURES_ON_DATA); sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: signers = PKCS7_get0_signers(p7, certs, flags); sl@0: sl@0: if(!signers) return 0; sl@0: sl@0: /* Now verify the certificates */ sl@0: sl@0: if (!(flags & PKCS7_NOVERIFY)) for (k = 0; k < sk_X509_num(signers); k++) { sl@0: signer = sk_X509_value (signers, k); sl@0: if (!(flags & PKCS7_NOCHAIN)) { sl@0: if(!X509_STORE_CTX_init(&cert_ctx, store, signer, sl@0: p7->d.sign->cert)) sl@0: { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); sl@0: sk_X509_free(signers); sl@0: return 0; sl@0: } sl@0: X509_STORE_CTX_set_purpose(&cert_ctx, sl@0: X509_PURPOSE_SMIME_SIGN); sl@0: } else if(!X509_STORE_CTX_init (&cert_ctx, store, signer, NULL)) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); sl@0: sk_X509_free(signers); sl@0: return 0; sl@0: } sl@0: if (!(flags & PKCS7_NOCRL)) sl@0: X509_STORE_CTX_set0_crls(&cert_ctx, p7->d.sign->crl); sl@0: i = X509_verify_cert(&cert_ctx); sl@0: if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx); sl@0: X509_STORE_CTX_cleanup(&cert_ctx); sl@0: if (i <= 0) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR); sl@0: ERR_add_error_data(2, "Verify error:", sl@0: X509_verify_cert_error_string(j)); sl@0: sk_X509_free(signers); sl@0: return 0; sl@0: } sl@0: /* Check for revocation status here */ sl@0: } sl@0: sl@0: /* Performance optimization: if the content is a memory BIO then sl@0: * store its contents in a temporary read only memory BIO. This sl@0: * avoids potentially large numbers of slow copies of data which will sl@0: * occur when reading from a read write memory BIO when signatures sl@0: * are calculated. sl@0: */ sl@0: sl@0: if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM)) sl@0: { sl@0: char *ptr; sl@0: long len; sl@0: len = BIO_get_mem_data(indata, &ptr); sl@0: tmpin = BIO_new_mem_buf(ptr, len); sl@0: if (tmpin == NULL) sl@0: { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); sl@0: return 0; sl@0: } sl@0: } sl@0: else sl@0: tmpin = indata; sl@0: sl@0: sl@0: if (!(p7bio=PKCS7_dataInit(p7,tmpin))) sl@0: goto err; sl@0: sl@0: if(flags & PKCS7_TEXT) { sl@0: if(!(tmpout = BIO_new(BIO_s_mem()))) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); sl@0: goto err; sl@0: } sl@0: } else tmpout = out; sl@0: sl@0: /* We now have to 'read' from p7bio to calculate digests etc. */ sl@0: for (;;) sl@0: { sl@0: i=BIO_read(p7bio,buf,sizeof(buf)); sl@0: if (i <= 0) break; sl@0: if (tmpout) BIO_write(tmpout, buf, i); sl@0: } sl@0: sl@0: if(flags & PKCS7_TEXT) { sl@0: if(!SMIME_text(tmpout, out)) { sl@0: PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SMIME_TEXT_ERROR); sl@0: BIO_free(tmpout); sl@0: goto err; sl@0: } sl@0: BIO_free(tmpout); sl@0: } sl@0: sl@0: /* Now Verify All Signatures */ sl@0: if (!(flags & PKCS7_NOSIGS)) sl@0: for (i=0; iissuer_and_serial; sl@0: signer = NULL; sl@0: /* If any certificates passed they take priority */ sl@0: if (certs) signer = X509_find_by_issuer_and_serial (certs, sl@0: ias->issuer, ias->serial); sl@0: if (!signer && !(flags & PKCS7_NOINTERN) sl@0: && p7->d.sign->cert) signer = sl@0: X509_find_by_issuer_and_serial (p7->d.sign->cert, sl@0: ias->issuer, ias->serial); sl@0: if (!signer) { sl@0: PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND); sl@0: sk_X509_free(signers); sl@0: return NULL; sl@0: } sl@0: sl@0: if (!sk_X509_push(signers, signer)) { sl@0: sk_X509_free(signers); sl@0: return NULL; sl@0: } sl@0: } sl@0: return signers; sl@0: } sl@0: sl@0: sl@0: /* Build a complete PKCS#7 enveloped data */ sl@0: sl@0: EXPORT_C PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, sl@0: int flags) sl@0: { sl@0: PKCS7 *p7; sl@0: BIO *p7bio = NULL; sl@0: int i; sl@0: X509 *x509; sl@0: if(!(p7 = PKCS7_new())) { sl@0: PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); sl@0: return NULL; sl@0: } sl@0: sl@0: if (!PKCS7_set_type(p7, NID_pkcs7_enveloped)) sl@0: goto err; sl@0: if(!PKCS7_set_cipher(p7, cipher)) { sl@0: PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_ERROR_SETTING_CIPHER); sl@0: goto err; sl@0: } sl@0: sl@0: for(i = 0; i < sk_X509_num(certs); i++) { sl@0: x509 = sk_X509_value(certs, i); sl@0: if(!PKCS7_add_recipient(p7, x509)) { sl@0: PKCS7err(PKCS7_F_PKCS7_ENCRYPT, sl@0: PKCS7_R_ERROR_ADDING_RECIPIENT); sl@0: goto err; sl@0: } sl@0: } sl@0: sl@0: if(!(p7bio = PKCS7_dataInit(p7, NULL))) { sl@0: PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); sl@0: goto err; sl@0: } sl@0: sl@0: SMIME_crlf_copy(in, p7bio, flags); sl@0: sl@0: (void)BIO_flush(p7bio); sl@0: sl@0: if (!PKCS7_dataFinal(p7,p7bio)) { sl@0: PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_PKCS7_DATAFINAL_ERROR); sl@0: goto err; sl@0: } sl@0: BIO_free_all(p7bio); sl@0: sl@0: return p7; sl@0: sl@0: err: sl@0: sl@0: BIO_free_all(p7bio); sl@0: PKCS7_free(p7); sl@0: return NULL; sl@0: sl@0: } sl@0: sl@0: EXPORT_C int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags) sl@0: { sl@0: BIO *tmpmem; sl@0: int ret, i; sl@0: #ifndef SYMBIAN sl@0: char buf[4096]; sl@0: #else sl@0: char buf[512]; sl@0: #endif sl@0: sl@0: if(!p7) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_INVALID_NULL_POINTER); sl@0: return 0; sl@0: } sl@0: sl@0: if(!PKCS7_type_is_enveloped(p7)) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_WRONG_CONTENT_TYPE); sl@0: return 0; sl@0: } sl@0: sl@0: if(cert && !X509_check_private_key(cert, pkey)) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT, sl@0: PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); sl@0: return 0; sl@0: } sl@0: sl@0: if(!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR); sl@0: return 0; sl@0: } sl@0: sl@0: if (flags & PKCS7_TEXT) { sl@0: BIO *tmpbuf, *bread; sl@0: /* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */ sl@0: if(!(tmpbuf = BIO_new(BIO_f_buffer()))) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); sl@0: BIO_free_all(tmpmem); sl@0: return 0; sl@0: } sl@0: if(!(bread = BIO_push(tmpbuf, tmpmem))) { sl@0: PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); sl@0: BIO_free_all(tmpbuf); sl@0: BIO_free_all(tmpmem); sl@0: return 0; sl@0: } sl@0: ret = SMIME_text(bread, data); sl@0: BIO_free_all(bread); sl@0: return ret; sl@0: } else { sl@0: for(;;) { sl@0: i = BIO_read(tmpmem, buf, sizeof(buf)); sl@0: if(i <= 0) break; sl@0: BIO_write(data, buf, i); sl@0: } sl@0: BIO_free_all(tmpmem); sl@0: return 1; sl@0: } sl@0: }