1/* pk7_smime.c */
2/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
3 * project 1999.
4 */
5/* ====================================================================
6 * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 *    software must display the following acknowledgment:
22 *    "This product includes software developed by the OpenSSL Project
23 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 *    endorse or promote products derived from this software without
27 *    prior written permission. For written permission, please contact
28 *    licensing@OpenSSL.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 *    nor may "OpenSSL" appear in their names without prior written
32 *    permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 *    acknowledgment:
36 *    "This product includes software developed by the OpenSSL Project
37 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com).  This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58
59/* Simple PKCS#7 processing functions */
60
61#include <stdio.h>
62#include "cryptlib.h"
63#include <openssl/x509.h>
64#include <openssl/x509v3.h>
65
66PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
67		  BIO *data, int flags)
68{
69	PKCS7 *p7;
70	PKCS7_SIGNER_INFO *si;
71	BIO *p7bio;
72	STACK_OF(X509_ALGOR) *smcap;
73	int i;
74
75	if(!X509_check_private_key(signcert, pkey)) {
76		PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
77                return NULL;
78	}
79
80	if(!(p7 = PKCS7_new())) {
81		PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
82		return NULL;
83	}
84
85	PKCS7_set_type(p7, NID_pkcs7_signed);
86
87	PKCS7_content_new(p7, NID_pkcs7_data);
88
89    	if (!(si = PKCS7_add_signature(p7,signcert,pkey,EVP_sha1()))) {
90		PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR);
91		return NULL;
92	}
93
94	if(!(flags & PKCS7_NOCERTS)) {
95		PKCS7_add_certificate(p7, signcert);
96		if(certs) for(i = 0; i < sk_X509_num(certs); i++)
97			PKCS7_add_certificate(p7, sk_X509_value(certs, i));
98	}
99
100	if(!(p7bio = PKCS7_dataInit(p7, NULL))) {
101		PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
102		return NULL;
103	}
104
105
106	SMIME_crlf_copy(data, p7bio, flags);
107
108	if(!(flags & PKCS7_NOATTR)) {
109		PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
110				V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data));
111		/* Add SMIMECapabilities */
112		if(!(flags & PKCS7_NOSMIMECAP))
113		{
114		if(!(smcap = sk_X509_ALGOR_new_null())) {
115			PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
116			return NULL;
117		}
118#ifndef OPENSSL_NO_DES
119		PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1);
120#endif
121#ifndef OPENSSL_NO_RC2
122		PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128);
123		PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64);
124#endif
125#ifndef OPENSSL_NO_DES
126		PKCS7_simple_smimecap (smcap, NID_des_cbc, -1);
127#endif
128#ifndef OPENSSL_NO_RC2
129		PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40);
130#endif
131		PKCS7_add_attrib_smimecap (si, smcap);
132		sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
133		}
134	}
135
136	if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1);
137
138        if (!PKCS7_dataFinal(p7,p7bio)) {
139		PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN);
140		return NULL;
141	}
142
143        BIO_free_all(p7bio);
144	return p7;
145}
146
147int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
148					BIO *indata, BIO *out, int flags)
149{
150	STACK_OF(X509) *signers;
151	X509 *signer;
152	STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
153	PKCS7_SIGNER_INFO *si;
154	X509_STORE_CTX cert_ctx;
155	char buf[4096];
156	int i, j=0, k, ret = 0;
157	BIO *p7bio;
158	BIO *tmpin, *tmpout;
159
160	if(!p7) {
161		PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_INVALID_NULL_POINTER);
162		return 0;
163	}
164
165	if(!PKCS7_type_is_signed(p7)) {
166		PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_WRONG_CONTENT_TYPE);
167		return 0;
168	}
169
170	/* Check for no data and no content: no data to verify signature */
171	if(PKCS7_get_detached(p7) && !indata) {
172		PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_CONTENT);
173		return 0;
174	}
175#if 0
176	/* NB: this test commented out because some versions of Netscape
177	 * illegally include zero length content when signing data.
178	 */
179
180	/* Check for data and content: two sets of data */
181	if(!PKCS7_get_detached(p7) && indata) {
182				PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CONTENT_AND_DATA_PRESENT);
183		return 0;
184	}
185#endif
186
187	sinfos = PKCS7_get_signer_info(p7);
188
189	if(!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) {
190		PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_SIGNATURES_ON_DATA);
191		return 0;
192	}
193
194
195	signers = PKCS7_get0_signers(p7, certs, flags);
196
197	if(!signers) return 0;
198
199	/* Now verify the certificates */
200
201	if (!(flags & PKCS7_NOVERIFY)) for (k = 0; k < sk_X509_num(signers); k++) {
202		signer = sk_X509_value (signers, k);
203		if (!(flags & PKCS7_NOCHAIN)) {
204			if(!X509_STORE_CTX_init(&cert_ctx, store, signer,
205							p7->d.sign->cert))
206				{
207				PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB);
208				sk_X509_free(signers);
209				return 0;
210				}
211			X509_STORE_CTX_set_purpose(&cert_ctx,
212						X509_PURPOSE_SMIME_SIGN);
213		} else if(!X509_STORE_CTX_init (&cert_ctx, store, signer, NULL)) {
214			PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB);
215			sk_X509_free(signers);
216			return 0;
217		}
218		i = X509_verify_cert(&cert_ctx);
219		if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx);
220		X509_STORE_CTX_cleanup(&cert_ctx);
221		if (i <= 0) {
222			PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR);
223			ERR_add_error_data(2, "Verify error:",
224					 X509_verify_cert_error_string(j));
225			sk_X509_free(signers);
226			return 0;
227		}
228		/* Check for revocation status here */
229	}
230
231	/* Performance optimization: if the content is a memory BIO then
232	 * store its contents in a temporary read only memory BIO. This
233	 * avoids potentially large numbers of slow copies of data which will
234	 * occur when reading from a read write memory BIO when signatures
235	 * are calculated.
236	 */
237
238	if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM))
239		{
240		char *ptr;
241		long len;
242		len = BIO_get_mem_data(indata, &ptr);
243		tmpin = BIO_new_mem_buf(ptr, len);
244		if (tmpin == NULL)
245			{
246			PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE);
247			return 0;
248			}
249		}
250	else
251		tmpin = indata;
252
253
254	p7bio=PKCS7_dataInit(p7,tmpin);
255
256	if(flags & PKCS7_TEXT) {
257		if(!(tmpout = BIO_new(BIO_s_mem()))) {
258			PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE);
259			goto err;
260		}
261	} else tmpout = out;
262
263	/* We now have to 'read' from p7bio to calculate digests etc. */
264	for (;;)
265	{
266		i=BIO_read(p7bio,buf,sizeof(buf));
267		if (i <= 0) break;
268		if (tmpout) BIO_write(tmpout, buf, i);
269	}
270
271	if(flags & PKCS7_TEXT) {
272		if(!SMIME_text(tmpout, out)) {
273			PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SMIME_TEXT_ERROR);
274			BIO_free(tmpout);
275			goto err;
276		}
277		BIO_free(tmpout);
278	}
279
280	/* Now Verify All Signatures */
281	if (!(flags & PKCS7_NOSIGS))
282	    for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sinfos); i++)
283		{
284		si=sk_PKCS7_SIGNER_INFO_value(sinfos,i);
285		signer = sk_X509_value (signers, i);
286		j=PKCS7_signatureVerify(p7bio,p7,si, signer);
287		if (j <= 0) {
288			PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SIGNATURE_FAILURE);
289			goto err;
290		}
291	}
292
293	ret = 1;
294
295	err:
296
297	if (tmpin == indata)
298		{
299		if(indata) BIO_pop(p7bio);
300		BIO_free_all(p7bio);
301		}
302	else
303		BIO_free_all(tmpin);
304
305	sk_X509_free(signers);
306
307	return ret;
308}
309
310STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, int flags)
311{
312	STACK_OF(X509) *signers;
313	STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
314	PKCS7_SIGNER_INFO *si;
315	PKCS7_ISSUER_AND_SERIAL *ias;
316	X509 *signer;
317	int i;
318
319	if(!p7) {
320		PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_INVALID_NULL_POINTER);
321		return NULL;
322	}
323
324	if(!PKCS7_type_is_signed(p7)) {
325		PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_WRONG_CONTENT_TYPE);
326		return NULL;
327	}
328
329	/* Collect all the signers together */
330
331	sinfos = PKCS7_get_signer_info(p7);
332
333	if(sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) {
334		PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_NO_SIGNERS);
335		return 0;
336	}
337
338	if(!(signers = sk_X509_new_null())) {
339		PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,ERR_R_MALLOC_FAILURE);
340		return NULL;
341	}
342
343	for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++)
344	{
345	    si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
346	    ias = si->issuer_and_serial;
347	    signer = NULL;
348		/* If any certificates passed they take priority */
349	    if (certs) signer = X509_find_by_issuer_and_serial (certs,
350					 	ias->issuer, ias->serial);
351	    if (!signer && !(flags & PKCS7_NOINTERN)
352			&& p7->d.sign->cert) signer =
353		              X509_find_by_issuer_and_serial (p7->d.sign->cert,
354					      	ias->issuer, ias->serial);
355	    if (!signer) {
356			PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND);
357			sk_X509_free(signers);
358			return 0;
359	    }
360
361	    sk_X509_push(signers, signer);
362	}
363	return signers;
364}
365
366
367/* Build a complete PKCS#7 enveloped data */
368
369PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher,
370								int flags)
371{
372	PKCS7 *p7;
373	BIO *p7bio = NULL;
374	int i;
375	X509 *x509;
376	if(!(p7 = PKCS7_new())) {
377		PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE);
378		return NULL;
379	}
380
381	PKCS7_set_type(p7, NID_pkcs7_enveloped);
382	if(!PKCS7_set_cipher(p7, cipher)) {
383		PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_ERROR_SETTING_CIPHER);
384		goto err;
385	}
386
387	for(i = 0; i < sk_X509_num(certs); i++) {
388		x509 = sk_X509_value(certs, i);
389		if(!PKCS7_add_recipient(p7, x509)) {
390			PKCS7err(PKCS7_F_PKCS7_ENCRYPT,
391					PKCS7_R_ERROR_ADDING_RECIPIENT);
392			goto err;
393		}
394	}
395
396	if(!(p7bio = PKCS7_dataInit(p7, NULL))) {
397		PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE);
398		goto err;
399	}
400
401	SMIME_crlf_copy(in, p7bio, flags);
402
403	BIO_flush(p7bio);
404
405        if (!PKCS7_dataFinal(p7,p7bio)) {
406		PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_PKCS7_DATAFINAL_ERROR);
407		goto err;
408	}
409        BIO_free_all(p7bio);
410
411	return p7;
412
413	err:
414
415	BIO_free(p7bio);
416	PKCS7_free(p7);
417	return NULL;
418
419}
420
421int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags)
422{
423	BIO *tmpmem;
424	int ret, i;
425	char buf[4096];
426
427	if(!p7) {
428		PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_INVALID_NULL_POINTER);
429		return 0;
430	}
431
432	if(!PKCS7_type_is_enveloped(p7)) {
433		PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_WRONG_CONTENT_TYPE);
434		return 0;
435	}
436
437	if(!X509_check_private_key(cert, pkey)) {
438		PKCS7err(PKCS7_F_PKCS7_DECRYPT,
439				PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
440		return 0;
441	}
442
443	if(!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) {
444		PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR);
445		return 0;
446	}
447
448	if (flags & PKCS7_TEXT) {
449		BIO *tmpbuf, *bread;
450		/* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */
451		if(!(tmpbuf = BIO_new(BIO_f_buffer()))) {
452			PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
453			return 0;
454		}
455		if(!(bread = BIO_push(tmpbuf, tmpmem))) {
456			PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
457			return 0;
458		}
459		ret = SMIME_text(bread, data);
460		BIO_free_all(bread);
461		return ret;
462	} else {
463		for(;;) {
464			i = BIO_read(tmpmem, buf, sizeof(buf));
465			if(i <= 0) break;
466			BIO_write(data, buf, i);
467		}
468		BIO_free_all(tmpmem);
469		return 1;
470	}
471}
472