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