1 /* smime-enc.c  -  Utility functions for performing S/MIME signatures
2  *                 and encryption.
3  *
4  * Copyright (c) 1999 Sampo Kellomaki <sampo@iki.fi>, All Rights Reserved.
5  * License: This software may be distributed under the same license
6  *          terms as openssl (i.e. free, but mandatory attribution).
7  *          See file LICENSE for details.
8  *
9  * 11.9.1999, Created. --Sampo
10  * 13.9.1999, 0.1 released. Now adding verify. --Sampo
11  * 1.10.1999, improved error handling, fixed decrypt --Sampo
12  * 6.10.1999, separated from smimeutil.c --Sampo
13  * 9.10.1999, reviewed for free problems --Sampo
14  *
15  * This module has adopted ideas and control flow from
16  *    openssl-0.9.4/crypto/pkcs7/sign.c
17  *    openssl-0.9.4/crypto/pkcs7/verify.c
18  *    openssl-0.9.4/crypto/pkcs7/enc.c
19  *    openssl-0.9.4/crypto/pkcs7/dec.c
20  * which are Copyright (c) 1995-1998 Eric Young (eay@cryptsoft.com),
21  * All rights reserved. See file LICENSE for conditions.
22  *
23  * This module has been developed to support a Lingo XTRA that is supposed
24  * to provide crypto functionality. It may, however, be useful for other
25  * purposes as well.
26  *
27  * This is a very simple S/MIME library. For example the multipart
28  * boundary separators are hard coded and no effort is made to verify
29  * that mime entities are in their canonical form before signing (the
30  * caller should make sure they are, canonical form means using CRLF
31  * as line termination, among other things). Also the multipart functionality
32  * only understands up to 3 attachments. For many tasks this is enough,
33  * but if its not, feel free to write more generic utilities.
34  *
35  * Memory management: most routines malloc the results. Freeing them is
36  * application's responsibility. I use libc malloc, but if in doubt
37  * it might be safer to just leak the memory (i.e. don't ever free it).
38  * This library works entirely in memory, so maximum memory consumption
39  * might be more than twice the total size of all files to be encrypted.
40  */
41 
42 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
43  * All rights reserved.
44  *
45  * This package is an SSL implementation written
46  * by Eric Young (eay@cryptsoft.com).
47  * The implementation was written so as to conform with Netscapes SSL.
48  *
49  * This library is free for commercial and non-commercial use as long as
50  * the following conditions are aheared to.  The following conditions
51  * apply to all code found in this distribution, be it the RC4, RSA,
52  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
53  * included with this distribution is covered by the same copyright terms
54  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
55  *
56  * Copyright remains Eric Young's, and as such any Copyright notices in
57  * the code are not to be removed.
58  * If this package is used in a product, Eric Young should be given attribution
59  * as the author of the parts of the library used.
60  * This can be in the form of a textual message at program startup or
61  * in documentation (online or textual) provided with the package.
62  *
63  * Redistribution and use in source and binary forms, with or without
64  * modification, are permitted provided that the following conditions
65  * are met:
66  * 1. Redistributions of source code must retain the copyright
67  *    notice, this list of conditions and the following disclaimer.
68  * 2. Redistributions in binary form must reproduce the above copyright
69  *    notice, this list of conditions and the following disclaimer in the
70  *    documentation and/or other materials provided with the distribution.
71  * 3. All advertising materials mentioning features or use of this software
72  *    must display the following acknowledgement:
73  *    "This product includes cryptographic software written by
74  *     Eric Young (eay@cryptsoft.com)"
75  *    The word 'cryptographic' can be left out if the rouines from the library
76  *    being used are not cryptographic related :-).
77  * 4. If you include any Windows specific code (or a derivative thereof) from
78  *   the apps directory (application code) you must include an acknowledgement:
79  *   "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
80  *
81  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
82  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
83  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
84  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
85  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
86  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
87  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
88  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
89  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
90  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
91  * SUCH DAMAGE.
92  *
93  * The licence and distribution terms for any publically available version or
94  * derivative of this code cannot be changed.  i.e. this code cannot simply be
95  * copied and put under another distribution licence
96  * [including the GNU Public Licence.]
97  */
98 
99 #include "platform.h"
100 
101 #include <stdio.h>
102 #include <string.h>
103 #include <time.h>
104 
105 #if defined(macintosh) || defined(__INTEL__)
106 #include "macglue.h"
107 #endif
108 
109 #include "logprint.h"
110 
111 #include <openssl/buffer.h>
112 #include <openssl/bio.h>
113 #include <openssl/x509.h>
114 #include <openssl/pem.h>
115 #include <openssl/err.h>
116 #include <openssl/rand.h>
117 
118 #define SMIME_INTERNALS  /* we want also our internal helper functions */
119 #include "smimeutil.h"
120 
121 /* ============= S I G N I N G   &   E N C R Y P T I O N ============= */
122 
123 /* Typically signing and encryption involves a sequence of calls like
124  *
125  *  msg = smime_encrypt(pubkey,
126  *           smime_clear_sign(privkey, password,
127  *              mime_mk_multipart(text, file1, len1, type1, name1,
128  *                                      NULL,  0,    NULL,  NULL,
129  *                                      NULL,  0,    NULL,  NULL)));
130  */
131 
132 /* Helper function for signing and signature verification. */
133 
134 /* Called by:  clear_sign, sign */
135 static BIO*
smime_sign_engine(X509 * x509,EVP_PKEY * pkey,const char * mime_entity,int detach)136 smime_sign_engine(X509* x509, EVP_PKEY* pkey,
137 		  const char* mime_entity, int detach)
138 {
139   int i;
140   char buf[4096];
141   BIO*  p7bio = NULL;
142   BIO*  bio = NULL;
143   PKCS7* p7 = NULL;
144   PKCS7_SIGNER_INFO* si;
145 
146   /* Set up BIOs and PKCS7 machinery */
147 
148   LOG_PRINT3("sig engine %x %x", x509, pkey);
149   LOG_PRINT3("           %x %d", mime_entity, detach);
150   if (!x509 || !pkey || !mime_entity) GOTO_ERR("NULL arg(s)");
151   if (!(bio = set_read_BIO_from_buf(mime_entity, -1))) goto err;
152   LOG_PRINT("PKCS7_new");
153   /*Log_malloc = Log;*/
154   if (!(p7=PKCS7_new())) GOTO_ERR("no memory?");
155   LOG_PRINT("PKCS7_set_type");
156   PKCS7_set_type(p7,NID_pkcs7_signed);
157   /*Log_malloc = NULL;*/
158   LOG_PRINT("Adding sig");
159   if (!(si=PKCS7_add_signature(p7,x509,pkey,EVP_sha1())))
160     GOTO_ERR("PKCS7_add_signature");
161 
162   LOG_PRINT("Adding signed attribute");
163   /* If you do this then you get signing time automatically added */
164   PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT,
165                              OBJ_nid2obj(NID_pkcs7_data));
166   /*PKCS7_add_certificate(p7,x509);*/
167   if (detach) PKCS7_set_detached(p7,1);
168 
169   /* Set the content of the signed to 'data' */
170   PKCS7_content_new(p7,NID_pkcs7_data);
171 
172   if (!(p7bio=PKCS7_dataInit(p7,NULL))) GOTO_ERR("PKCS7_dataInit");
173 
174   /* pump data from file to special PKCS7 BIO. This hashes it. */
175 
176   LOG_PRINT("pumping");
177 
178   for (;;)    {
179     i=BIO_read(bio,buf,sizeof(buf));
180     if (i <= 0) break;
181     BIO_write(p7bio,buf,i);
182   }
183   BIO_flush(p7bio);
184 
185   LOG_PRINT("data final...");
186   if (!PKCS7_dataFinal(p7,p7bio)) GOTO_ERR("PKCS7_dataFinal");
187   BIO_free_all(p7bio);
188   p7bio = NULL;
189   BIO_free_all(bio);
190 
191   if (!(bio = BIO_new(BIO_s_mem()))) GOTO_ERR("no memory?");
192   LOG_PRINT("Writing data to bio");
193   PEM_write_bio_PKCS7(bio,p7);
194   BIO_flush(bio);
195   PKCS7_free(p7);
196 
197   LOG_PRINT2("sig engine done %x", bio);
198   return bio;  /* return written memory bio (must be freed by caller)
199 		* caller will extract the data from buffer. */
200 
201 err:
202   if (p7bio) BIO_free_all(p7bio);
203   if (p7)    PKCS7_free(p7);
204   if (bio)   BIO_free_all(bio);
205   LOG_PRINT("sig engine error");
206   return NULL;
207 }
208 
209 /* Sign a mime entity, such as produced by mime_mk_multipart(). Signature
210  * is stored as separate mime entity so the message proper stays visible.
211  * I canonicalize the entity (LF->CRLF) because correct signature
212  * verification depends on this.
213  */
214 
215 /*
216 MIME-Version: 1.0
217 Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg
218 =sha1; boundary=sig42
219 
220 --sig42
221 Content-Type: text/plain
222 
223 message to be signed
224 --sig42
225 Content-Type: application/x-pkcs7-signature; name="smime.p7s"
226 Content-Transfer-Encoding: base64
227 Content-Disposition: attachment; filename="smime.p7s"
228 Content-Description: S/MIME Clear Signed Message
229 
230 MIAGCSqGSIb3DQEHA6CAMIIIZQIBADGCATcwggEzAgEAMIGbMIGVMQswCQYDVQQG
231 EwJQVDEPMA0GA1UEBxMGTGlzYm9hMRcwFQYDVQQKEw5OZXVyb25pbywgTGRhLjEZ
232 G0DXAj0zd/4AAAAA==
233 --sig42--
234 */
235 
236 /* Called by:  smime_clear_sign */
237 char*  /* returns smime encoded clear signed blob, or NULL if error */
clear_sign(X509 * x509,EVP_PKEY * pkey,const char * mime_entity)238 clear_sign(X509* x509, EVP_PKEY* pkey, const char* mime_entity)
239 {
240   char* b;
241   char* b64;
242   BIO*  wbio = 0;
243   int   n;
244 
245   LOG_PRINT("clear sig, canon entity...");
246   if (!(mime_entity = mime_canon(mime_entity))) goto err;
247   LOG_PRINT("clear sig, entity canoned. Now sig engine");
248 
249   /* Run crypto stuff over the mime_entity */
250 
251   if (!(wbio = smime_sign_engine(x509, pkey, mime_entity, 1))) goto err;
252   LOG_PRINT("clear sig: signed, now get data");
253   n = BIO_get_mem_data(wbio,&b64);
254   LOG_PRINT("clear sig: cut pem markers...");
255   if (!(b64 = cut_pem_markers_off(b64, n, "PKCS7"))) goto err;
256 
257   /* Wrap up the result in multipart/signed object */
258 
259   if (!(b = smime_mk_multipart_signed(mime_entity, b64))) goto err;
260 
261   LOG_PRINT("clear sig: done. free bio");
262   BIO_free_all(wbio);  /* this will also free b64 because b64 hangs from bio */
263   return b;
264 
265 err:
266   if (wbio) BIO_free_all(wbio);
267   return NULL;
268 }
269 
270 /* Called by:  main x2 */
271 char*
smime_clear_sign(const char * privkey,const char * password,const char * mime_entity)272 smime_clear_sign(const char* privkey,
273 		 const char* password,
274 		 const char* mime_entity)
275 {
276   char* b = NULL;
277   X509* x509 = NULL;
278   EVP_PKEY* pkey = NULL;
279 
280   /* Get key and certificate (why do we need both?) */
281 
282   if (!(pkey = open_private_key(privkey, password))) goto err;
283   if (!(x509 = extract_certificate(privkey))) goto err;
284   if (!(b = clear_sign(x509, pkey, mime_entity))) goto err;
285 
286 err:
287   if (pkey)  EVP_PKEY_free(pkey);
288   if (x509)  X509_free(x509);
289   return b;
290 }
291 
292 /* Sign a mime entity, such as produced by mime_mk_multipart(). Signature
293  * and entity are output as one base64 blob so the entity is not trivially
294  * visible. */
295 
296 /*
297 MIME-Version: 1.0
298 Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
299 Content-Transfer-Encoding: base64
300 Content-Disposition: attachment; filename="smime.p7m"
301 Content-Description: S/MIME Signed Message
302 
303 MIAGCSqGSIb3DQEHA6CAMIIIZQIBADGCATcwggEzAgEAMIGbMIGVMQswCQYDVQQG
304 EwJQVDEPMA0GA1UEBxMGTGlzYm9hMRcwFQYDVQQKEw5OZXVyb25pbywgTGRhLjEZ
305 G0DXAj0zd/4AAAAA==
306  */
307 
308 /* Called by:  smime_sign */
309 char*  /* returns smime blob, NULL if error */
sign(X509 * x509,EVP_PKEY * pkey,const char * mime_entity)310 sign(X509* x509, EVP_PKEY* pkey, const char* mime_entity)
311 {
312   char* b;
313   char* b64;
314   BIO*  wbio;
315   int   n;
316 
317   mime_entity = mime_canon(mime_entity);
318 
319   /* Run crypto stuff over the mime_entity */
320 
321   if (!(wbio = smime_sign_engine(x509, pkey, mime_entity, 0))) goto err;
322   n = BIO_get_mem_data(wbio,&b64);
323   if (!(b64 = cut_pem_markers_off(b64, n, "PKCS7"))) goto err;
324 
325   /* Add headers */
326 
327   if (!(b = strdup("Content-type: application/x-pkcs7-mime; name=\"smime.p7m\"" CRLF
328 		   "Content-transfer-encoding: base64" CRLF
329 		   "Content-Disposition: attachment; filename=\"smime.p7m\"" CRLF
330 		   CRLF))) GOTO_ERR("no memory?");
331   if (!(b = concat(b, b64))) GOTO_ERR("no memory?");
332 
333   BIO_free_all(wbio);  /* also frees b64 */
334   return b;
335 
336 err:
337   if (wbio) BIO_free_all(wbio);
338   return NULL;
339 }
340 
341 /* Called by:  main */
342 char*
smime_sign(const char * privkey,const char * password,const char * mime_entity)343 smime_sign(const char* privkey, const char* password, const char* mime_entity)
344 {
345   char* b = NULL;
346   X509* x509 = NULL;
347   EVP_PKEY* pkey = NULL;
348 
349   /* Get key and certificate (why do we need both?) */
350 
351   if (!(pkey = open_private_key(privkey, password))) goto err;
352   if (!(x509 = extract_certificate(privkey))) goto err;
353   if (!(b = sign(x509, pkey, mime_entity))) goto err;
354 
355 err:
356   if (pkey)  EVP_PKEY_free(pkey);
357   if (x509)  X509_free(x509);
358   return b;
359 }
360 
361 /* Encrypt a mime entity such as produced by smime_clear_sign(). */
362 
363 /*
364 MIME-Version: 1.0
365 Content-Type: application/x-pkcs7-mime; name="smime.p7m"
366 Content-Transfer-Encoding: base64
367 Content-Disposition: attachment; filename="smime.p7m"
368 Content-Description: S/MIME Encrypted Message
369 
370 MIAGCSqGSIb3DQEHA6CAMIIIZQIBADGCATcwggEzAgEAMIGbMIGVMQswCQYDVQQG
371 EwJQVDEPMA0GA1UEBxMGTGlzYm9hMRcwFQYDVQQKEw5OZXVyb25pbywgTGRhLjEZ
372 G0DXAj0zd/4AAAAA==
373  */
374 
375 /* Called by:  smime_encrypt */
376 char*
encrypt1(X509 * x509,const char * mime_entity)377 encrypt1(X509* x509, const char* mime_entity)
378 {
379   time_t t;
380   char* b;
381   char* b64;
382   int   i, n;
383   char  buf[4096];
384   BIO*  p7bio = NULL;
385   BIO*  rbio = NULL;
386   BIO*  wbio = NULL;
387   PKCS7* p7 = NULL;;
388 
389   t = time(NULL);
390   RAND_seed(&t,sizeof(t));
391 #ifdef WINDOWS
392   RAND_screen(); /* Loading video display memory into random state */
393 #endif
394 
395   LOG_PRINT3("encrypt1", x509, mime_entity);
396 
397   /* Set up BIOs and PKCS7 machinery */
398 
399   if (!(rbio = set_read_BIO_from_buf(mime_entity, -1))) goto err;
400 
401   if (!(p7=PKCS7_new())) GOTO_ERR("no memory?");
402   PKCS7_set_type(p7,NID_pkcs7_enveloped);
403 
404 #if 1
405   if (!PKCS7_set_cipher(p7,EVP_des_ede3_cbc()))
406     GOTO_ERR("PKCS7_set_cipher des-ede3-cbc");
407 #else
408   /* SECURITY CAVEAT: weak cipher by default */
409   if (!PKCS7_set_cipher(p7,EVP_rc2_40_cbc()))
410     GOTO_ERR("PKCS7_set_cipher rc2-40-cbc");
411 #endif
412 
413   LOG_PRINT("encrypt1: add recipient");
414 
415   if (!PKCS7_add_recipient(p7,x509)) GOTO_ERR("PKCS7_add_recipient");
416 
417   LOG_PRINT("encrypt1: data init");
418   if (!(p7bio=PKCS7_dataInit(p7,NULL))) GOTO_ERR("PKCS7_dataInit");
419 
420   /* pump data from file to special PKCS7 BIO. This encrypts it. */
421 
422   LOG_PRINT("encrypt1: pump");
423   for (;;)    {
424     i=BIO_read(rbio,buf,sizeof(buf));
425     if (i <= 0) break;
426     BIO_write(p7bio,buf,i);
427   }
428   BIO_flush(p7bio);
429 
430   LOG_PRINT("encrypt1: dataFinal");
431   if (!PKCS7_dataFinal(p7,p7bio)) GOTO_ERR("PKCS7_dataFinal");
432   BIO_free_all(rbio);
433   BIO_free_all(p7bio);
434   rbio = p7bio = NULL;
435 
436   if (!(wbio = BIO_new(BIO_s_mem()))) GOTO_ERR("no memory?");
437   LOG_PRINT("encrypt1: write bio");
438   PEM_write_bio_PKCS7(wbio,p7);
439   BIO_flush(wbio);
440   PKCS7_free(p7);
441   p7 = NULL;
442 
443   LOG_PRINT("encrypt1: cutting markers");
444   n = BIO_get_mem_data(wbio,&b64);
445   b64 = cut_pem_markers_off(b64, n, "PKCS7");
446 
447   LOG_PRINT("encrypt1: wrapping in headers");
448   if (!(b = strdup("Content-type: application/x-pkcs7-mime; name=\"smime.p7m\""
449 		   CRLF
450 		   "Content-transfer-encoding: base64" CRLF
451 		   "Content-Disposition: attachment; filename=\"smime.p7m\""
452 		   CRLF CRLF)))  GOTO_ERR("no memory?");
453   if (!(b = concat(b, b64)))  GOTO_ERR("no memory?");
454 
455   BIO_free_all(wbio);  /* also frees b64 */
456   LOG_PRINT("encrypt1: OK");
457 
458   t = time(NULL);
459   RAND_seed(&t,sizeof(t));
460   RAND_write_file(randomfile);
461 
462   return b; /* return value must be freed by caller */
463 
464 err:
465   if (rbio)  BIO_free_all(rbio);
466   if (p7bio) BIO_free_all(p7bio);
467   if (wbio)  BIO_free_all(wbio);
468   if (p7)    PKCS7_free(p7);
469   return NULL;
470 }
471 
472 /* Called by:  main */
473 char*
smime_encrypt(const char * pubkey,const char * mime_entity)474 smime_encrypt(const char* pubkey, const char* mime_entity)
475 {
476   char* b = NULL;
477   X509* x509;
478 
479   /* Get certificate */
480 
481   if (!(x509 = extract_certificate(pubkey))) goto err;
482   b = encrypt1(x509, mime_entity);
483 
484 err:
485   //if (x509) X509_free(x509);
486   return b; /* return value must be freed by caller */
487 }
488 
489 /* ================= base64 encoding and decoding ================= */
490 
491 /* Called by:  attach, main x2, mime_base64_entity */
492 int /* returns number of bytes in result. b64 is NULL terminated */
smime_base64(int encp,const char * data,int len,char ** out)493 smime_base64(int encp /* true == encode */, const char* data, int len, char** out)
494 {
495   BIO* b64bio;
496   BIO* rbio = NULL;
497   BIO* wbio = NULL;
498   char buf[4096];
499   int  n = -1;
500 
501   if (out) *out = NULL;
502   if (!data || !out) GOTO_ERR("NULL arg");
503 
504   /* create two memory buffer BIOs */
505 
506   if (!(rbio = set_read_BIO_from_buf(data, len))) goto err;
507   if (!(wbio = BIO_new(BIO_s_mem()))) GOTO_ERR("no memory?");
508 
509   /* insert base64 filter */
510 
511   if (!(b64bio=BIO_new(BIO_f_base64()))) GOTO_ERR("no memory?");
512   if (encp)
513     wbio=BIO_push(b64bio,wbio);
514   else
515     rbio=BIO_push(b64bio,rbio);
516 
517   /* pump stuff through filter */
518 
519   for (;;) {
520     if ((n=BIO_read(rbio,buf,sizeof(buf))) <= 0) break;
521     if (BIO_write(wbio, buf,n) != n) GOTO_ERR("no memory? (base64 pump)");
522   }
523 
524   /* free BIOs and return base64 encoded block */
525 
526   n = get_written_BIO_data(wbio, out); /* if error (-1) will just propagate */
527 
528 err:
529   if (wbio) BIO_free_all(wbio);  /* b64bio is freed as part of the stack */
530   if (rbio) BIO_free_all(rbio);
531   return n;
532 }
533 
534 /* EOF  -  smime-enc.c */
535