Imported Upstream version 3.13.6
[platform/upstream/nss.git] / mozilla / security / nss / lib / pkcs7 / p7common.c
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is the Netscape security libraries.
15  *
16  * The Initial Developer of the Original Code is
17  * Netscape Communications Corporation.
18  * Portions created by the Initial Developer are Copyright (C) 1994-2000
19  * the Initial Developer. All Rights Reserved.
20  *
21  * Contributor(s):
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */
36
37 /*
38  * PKCS7 implementation -- the exported parts that are used whether
39  * creating or decoding.
40  *
41  * $Id: p7common.c,v 1.8 2010/04/04 20:50:52 nelson%bolyard.com Exp $
42  */
43
44 #include "p7local.h"
45
46 #include "cert.h"
47 #include "secitem.h"
48 #include "secoid.h"
49 #include "pk11func.h"
50
51 /*
52  * Find out (saving pointer to lookup result for future reference)
53  * and return the inner content type.
54  */
55 SECOidTag
56 SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo)
57 {
58     if (cinfo->contentTypeTag == NULL)
59         cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
60
61     if (cinfo->contentTypeTag == NULL)
62         return SEC_OID_UNKNOWN;
63
64     return cinfo->contentTypeTag->offset;
65 }
66
67
68 /*
69  * Destroy a PKCS7 contentInfo and all of its sub-pieces.
70  */
71 void
72 SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo)
73 {
74     SECOidTag kind;
75     CERTCertificate **certs;
76     CERTCertificateList **certlists;
77     SEC_PKCS7SignerInfo **signerinfos;
78     SEC_PKCS7RecipientInfo **recipientinfos;
79
80     PORT_Assert (cinfo->refCount > 0);
81     if (cinfo->refCount <= 0)
82         return;
83
84     cinfo->refCount--;
85     if (cinfo->refCount > 0)
86         return;
87
88     certs = NULL;
89     certlists = NULL;
90     recipientinfos = NULL;
91     signerinfos = NULL;
92
93     kind = SEC_PKCS7ContentType (cinfo);
94     switch (kind) {
95       case SEC_OID_PKCS7_ENVELOPED_DATA:
96         {
97             SEC_PKCS7EnvelopedData *edp;
98
99             edp = cinfo->content.envelopedData;
100             if (edp != NULL) {
101                 recipientinfos = edp->recipientInfos;
102             }
103         }
104         break;
105       case SEC_OID_PKCS7_SIGNED_DATA:
106         {
107             SEC_PKCS7SignedData *sdp;
108
109             sdp = cinfo->content.signedData;
110             if (sdp != NULL) {
111                 certs = sdp->certs;
112                 certlists = sdp->certLists;
113                 signerinfos = sdp->signerInfos;
114             }
115         }
116         break;
117       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
118         {
119             SEC_PKCS7SignedAndEnvelopedData *saedp;
120
121             saedp = cinfo->content.signedAndEnvelopedData;
122             if (saedp != NULL) {
123                 certs = saedp->certs;
124                 certlists = saedp->certLists;
125                 recipientinfos = saedp->recipientInfos;
126                 signerinfos = saedp->signerInfos;
127                 if (saedp->sigKey != NULL)
128                     PK11_FreeSymKey (saedp->sigKey);
129             }
130         }
131         break;
132       default:
133         /* XXX Anything else that needs to be "manually" freed/destroyed? */
134         break;
135     }
136
137     if (certs != NULL) {
138         CERTCertificate *cert;
139
140         while ((cert = *certs++) != NULL) {
141             CERT_DestroyCertificate (cert);
142         }
143     }
144
145     if (certlists != NULL) {
146         CERTCertificateList *certlist;
147
148         while ((certlist = *certlists++) != NULL) {
149             CERT_DestroyCertificateList (certlist);
150         }
151     }
152
153     if (recipientinfos != NULL) {
154         SEC_PKCS7RecipientInfo *ri;
155
156         while ((ri = *recipientinfos++) != NULL) {
157             if (ri->cert != NULL)
158                 CERT_DestroyCertificate (ri->cert);
159         }
160     }
161
162     if (signerinfos != NULL) {
163         SEC_PKCS7SignerInfo *si;
164
165         while ((si = *signerinfos++) != NULL) {
166             if (si->cert != NULL)
167                 CERT_DestroyCertificate (si->cert);
168             if (si->certList != NULL)
169                 CERT_DestroyCertificateList (si->certList);
170         }
171     }
172
173     if (cinfo->poolp != NULL) {
174         PORT_FreeArena (cinfo->poolp, PR_FALSE);        /* XXX clear it? */
175     }
176 }
177
178
179 /*
180  * Return a copy of the given contentInfo.  The copy may be virtual
181  * or may be real -- either way, the result needs to be passed to
182  * SEC_PKCS7DestroyContentInfo later (as does the original).
183  */
184 SEC_PKCS7ContentInfo *
185 SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo)
186 {
187     if (cinfo == NULL)
188         return NULL;
189
190     PORT_Assert (cinfo->refCount > 0);
191
192     if (cinfo->created) {
193         /*
194          * Want to do a real copy of these; otherwise subsequent
195          * changes made to either copy are likely to be a surprise.
196          * XXX I suspect that this will not actually be called for yet,
197          * which is why the assert, so to notice if it is...
198          */
199         PORT_Assert (0);
200         /*
201          * XXX Create a new pool here, and copy everything from
202          * within.  For cert stuff, need to call the appropriate
203          * copy functions, etc.
204          */
205     }
206
207     cinfo->refCount++;
208     return cinfo;
209 }
210
211
212 /*
213  * Return a pointer to the actual content.  In the case of those types
214  * which are encrypted, this returns the *plain* content.
215  * XXX Needs revisiting if/when we handle nested encrypted types.
216  */
217 SECItem *
218 SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo)
219 {
220     SECOidTag kind;
221
222     kind = SEC_PKCS7ContentType (cinfo);
223     switch (kind) {
224       case SEC_OID_PKCS7_DATA:
225         return cinfo->content.data;
226       case SEC_OID_PKCS7_DIGESTED_DATA:
227         {
228             SEC_PKCS7DigestedData *digd;
229
230             digd = cinfo->content.digestedData;
231             if (digd == NULL)
232                 break;
233             return SEC_PKCS7GetContent (&(digd->contentInfo));
234         }
235       case SEC_OID_PKCS7_ENCRYPTED_DATA:
236         {
237             SEC_PKCS7EncryptedData *encd;
238
239             encd = cinfo->content.encryptedData;
240             if (encd == NULL)
241                 break;
242             return &(encd->encContentInfo.plainContent);
243         }
244       case SEC_OID_PKCS7_ENVELOPED_DATA:
245         {
246             SEC_PKCS7EnvelopedData *envd;
247
248             envd = cinfo->content.envelopedData;
249             if (envd == NULL)
250                 break;
251             return &(envd->encContentInfo.plainContent);
252         }
253       case SEC_OID_PKCS7_SIGNED_DATA:
254         {
255             SEC_PKCS7SignedData *sigd;
256
257             sigd = cinfo->content.signedData;
258             if (sigd == NULL)
259                 break;
260             return SEC_PKCS7GetContent (&(sigd->contentInfo));
261         }
262       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
263         {
264             SEC_PKCS7SignedAndEnvelopedData *saed;
265
266             saed = cinfo->content.signedAndEnvelopedData;
267             if (saed == NULL)
268                 break;
269             return &(saed->encContentInfo.plainContent);
270         }
271       default:
272         PORT_Assert(0);
273         break;
274     }
275
276     return NULL;
277 }
278
279
280 /*
281  * XXX Fix the placement and formatting of the
282  * following routines (i.e. make them consistent with the rest of
283  * the pkcs7 code -- I think some/many belong in other files and
284  * they all need a formatting/style rehaul)
285  */
286
287 /* retrieve the algorithm identifier for encrypted data.  
288  * the identifier returned is a copy of the algorithm identifier
289  * in the content info and needs to be freed after being used.
290  *
291  *   cinfo is the content info for which to retrieve the
292  *     encryption algorithm.
293  *
294  * if the content info is not encrypted data or an error 
295  * occurs NULL is returned.
296  */
297 SECAlgorithmID *
298 SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo)
299 {
300   SECAlgorithmID *alg = 0;
301   switch (SEC_PKCS7ContentType(cinfo))
302     {
303     case SEC_OID_PKCS7_ENCRYPTED_DATA:
304       alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg;
305       break;
306     case SEC_OID_PKCS7_ENVELOPED_DATA:
307       alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg;
308       break;
309     case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
310       alg = &cinfo->content.signedAndEnvelopedData
311         ->encContentInfo.contentEncAlg;
312       break;
313     default:
314       alg = 0;
315       break;
316     }
317
318     return alg;
319 }
320
321 /* set the content of the content info.  For data content infos,
322  * the data is set.  For encrytped content infos, the plainContent
323  * is set, and is expected to be encrypted later.
324  *  
325  * cinfo is the content info where the data will be set
326  *
327  * buf is a buffer of the data to set
328  *
329  * len is the length of the data being set.
330  *
331  * in the event of an error, SECFailure is returned.  SECSuccess 
332  * indicates the content was successfully set.
333  */
334 SECStatus 
335 SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo,
336                     const char *buf, 
337                     unsigned long len)
338 {
339     SECOidTag cinfo_type;
340     SECStatus rv;
341     SECItem content;
342     SECOidData *contentTypeTag = NULL;
343
344     content.type = siBuffer;
345     content.data = (unsigned char *)buf;
346     content.len = len;
347
348     cinfo_type = SEC_PKCS7ContentType(cinfo);
349
350     /* set inner content */
351     switch(cinfo_type)
352     {
353         case SEC_OID_PKCS7_SIGNED_DATA:
354             if(content.len > 0) {
355                 /* we "leak" the old content here, but as it's all in the pool */
356                 /* it does not really matter */
357
358                 /* create content item if necessary */
359                 if (cinfo->content.signedData->contentInfo.content.data == NULL)
360                     cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0);
361                 rv = SECITEM_CopyItem(cinfo->poolp, 
362                         cinfo->content.signedData->contentInfo.content.data,
363                         &content);
364             } else {
365                 cinfo->content.signedData->contentInfo.content.data->data = NULL;
366                 cinfo->content.signedData->contentInfo.content.data->len = 0;
367                 rv = SECSuccess;
368             }
369             if(rv == SECFailure)
370                 goto loser;
371             
372             break;
373         case SEC_OID_PKCS7_ENCRYPTED_DATA:
374             /* XXX this forces the inner content type to be "data" */
375             /* do we really want to override without asking or reason? */
376             contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA);
377             if(contentTypeTag == NULL)
378                 goto loser;
379             rv = SECITEM_CopyItem(cinfo->poolp, 
380                 &(cinfo->content.encryptedData->encContentInfo.contentType),
381                 &(contentTypeTag->oid));
382             if(rv == SECFailure)
383                 goto loser;
384             if(content.len > 0) {
385                 rv = SECITEM_CopyItem(cinfo->poolp, 
386                         &(cinfo->content.encryptedData->encContentInfo.plainContent),
387                         &content);
388             } else {
389                 cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL;
390                 cinfo->content.encryptedData->encContentInfo.encContent.data = NULL;
391                 cinfo->content.encryptedData->encContentInfo.plainContent.len = 0;
392                 cinfo->content.encryptedData->encContentInfo.encContent.len = 0;
393                 rv = SECSuccess;
394             }
395             if(rv == SECFailure)
396                 goto loser;
397             break;
398         case SEC_OID_PKCS7_DATA:
399             cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp,
400                 sizeof(SECItem));
401             if(cinfo->content.data == NULL)
402                 goto loser;
403             if(content.len > 0) {
404                 rv = SECITEM_CopyItem(cinfo->poolp,
405                         cinfo->content.data, &content);
406             } else {
407                 /* handle case with NULL content */
408                 rv = SECSuccess;
409             }
410             if(rv == SECFailure)
411                 goto loser;
412             break;
413         default:
414             goto loser;
415     }
416
417     return SECSuccess;
418
419 loser:
420         
421     return SECFailure;
422 }
423
424 /* the content of an encrypted data content info is encrypted.
425  * it is assumed that for encrypted data, that the data has already
426  * been set and is in the "plainContent" field of the content info.
427  *
428  * cinfo is the content info to encrypt
429  *
430  * key is the key with which to perform the encryption.  if the
431  *     algorithm is a password based encryption algorithm, the
432  *     key is actually a password which will be processed per
433  *     PKCS #5.
434  * 
435  * in the event of an error, SECFailure is returned.  SECSuccess
436  * indicates a success.
437  */
438 SECStatus 
439 SEC_PKCS7EncryptContents(PRArenaPool *poolp,
440                          SEC_PKCS7ContentInfo *cinfo,
441                          SECItem *key,
442                          void *wincx)
443 {
444     SECAlgorithmID *algid       = NULL;
445     SECItem *       result      = NULL;
446     SECItem *       src;
447     SECItem *       dest;
448     SECItem *       blocked_data = NULL;
449     void *          mark;
450     void *          cx;
451     PK11SymKey *    eKey        = NULL;
452     PK11SlotInfo *  slot        = NULL;
453
454     CK_MECHANISM_TYPE cryptoMechType;
455     int             bs;
456     SECStatus       rv          = SECFailure;
457     SECItem         *c_param = NULL;
458
459     if((cinfo == NULL) || (key == NULL))
460         return SECFailure;
461
462     if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
463         return SECFailure;
464
465     algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);     
466     if(algid == NULL)
467         return SECFailure;
468
469     if(poolp == NULL)
470         poolp = cinfo->poolp;
471
472     mark = PORT_ArenaMark(poolp);
473     
474     src = &cinfo->content.encryptedData->encContentInfo.plainContent;
475     dest = &cinfo->content.encryptedData->encContentInfo.encContent;
476     dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
477     dest->len = (src->len + 64);
478     if(dest->data == NULL) {
479         rv = SECFailure;
480         goto loser;
481     }
482
483     slot = PK11_GetInternalKeySlot();
484     if(slot == NULL) {
485         rv = SECFailure;
486         goto loser;
487     }
488
489     eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
490     if(eKey == NULL) {
491         rv = SECFailure;
492         goto loser;
493     }
494     
495     cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
496     if (cryptoMechType == CKM_INVALID_MECHANISM) {
497         rv = SECFailure;
498         goto loser;
499     }
500
501     /* block according to PKCS 8 */
502     bs = PK11_GetBlockSize(cryptoMechType, c_param);
503     rv = SECSuccess;
504     if(bs) {
505         char pad_char;
506         pad_char = (char)(bs - (src->len % bs));
507         if(src->len % bs) {
508             rv = SECSuccess;
509             blocked_data = PK11_BlockData(src, bs);
510             if(blocked_data) {
511                 PORT_Memset((blocked_data->data + blocked_data->len 
512                             - (int)pad_char), 
513                             pad_char, (int)pad_char);
514             } else {
515                 rv = SECFailure;
516                 goto loser;
517             }
518         } else {
519             blocked_data = SECITEM_DupItem(src);
520             if(blocked_data) {
521                 blocked_data->data = (unsigned char*)PORT_Realloc(
522                                                   blocked_data->data,
523                                                   blocked_data->len + bs);
524                 if(blocked_data->data) {
525                     blocked_data->len += bs;
526                     PORT_Memset((blocked_data->data + src->len), (char)bs, bs);
527                 } else {
528                     rv = SECFailure;
529                     goto loser;
530                 }
531             } else {
532                 rv = SECFailure;
533                 goto loser;
534             }
535          }
536     } else {
537         blocked_data = SECITEM_DupItem(src);
538         if(!blocked_data) {
539             rv = SECFailure;
540             goto loser;
541         }
542     }
543
544     cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
545                                     eKey, c_param);
546     if(cx == NULL) {
547         rv = SECFailure;
548         goto loser;
549     }
550
551     rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), 
552                        (int)(src->len + 64), blocked_data->data, 
553                        (int)blocked_data->len);
554     PK11_DestroyContext((PK11Context*)cx, PR_TRUE);
555
556 loser:
557     /* let success fall through */
558     if(blocked_data != NULL)
559         SECITEM_ZfreeItem(blocked_data, PR_TRUE);
560
561     if(result != NULL)
562         SECITEM_ZfreeItem(result, PR_TRUE);
563
564     if(rv == SECFailure)
565         PORT_ArenaRelease(poolp, mark);
566     else 
567         PORT_ArenaUnmark(poolp, mark);
568
569     if(eKey != NULL)
570         PK11_FreeSymKey(eKey);
571
572     if(slot != NULL)
573         PK11_FreeSlot(slot);
574
575     if(c_param != NULL) 
576         SECITEM_ZfreeItem(c_param, PR_TRUE);
577         
578     return rv;
579 }
580
581 /* the content of an encrypted data content info is decrypted.
582  * it is assumed that for encrypted data, that the data has already
583  * been set and is in the "encContent" field of the content info.
584  *
585  * cinfo is the content info to decrypt
586  *
587  * key is the key with which to perform the decryption.  if the
588  *     algorithm is a password based encryption algorithm, the
589  *     key is actually a password which will be processed per
590  *     PKCS #5.
591  * 
592  * in the event of an error, SECFailure is returned.  SECSuccess
593  * indicates a success.
594  */
595 SECStatus 
596 SEC_PKCS7DecryptContents(PRArenaPool *poolp,
597                          SEC_PKCS7ContentInfo *cinfo,
598                          SECItem *key,
599                          void *wincx)
600 {
601     SECAlgorithmID *algid = NULL;
602     SECStatus rv = SECFailure;
603     SECItem *result = NULL, *dest, *src;
604     void *mark;
605
606     PK11SymKey *eKey = NULL;
607     PK11SlotInfo *slot = NULL;
608     CK_MECHANISM_TYPE cryptoMechType;
609     void *cx;
610     SECItem *c_param = NULL;
611     int bs;
612
613     if((cinfo == NULL) || (key == NULL))
614         return SECFailure;
615
616     if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
617         return SECFailure;
618
619     algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);     
620     if(algid == NULL)
621         return SECFailure;
622
623     if(poolp == NULL)
624         poolp = cinfo->poolp;
625
626     mark = PORT_ArenaMark(poolp);
627     
628     src = &cinfo->content.encryptedData->encContentInfo.encContent;
629     dest = &cinfo->content.encryptedData->encContentInfo.plainContent;
630     dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
631     dest->len = (src->len + 64);
632     if(dest->data == NULL) {
633         rv = SECFailure;
634         goto loser;
635     }
636
637     slot = PK11_GetInternalKeySlot();
638     if(slot == NULL) {
639         rv = SECFailure;
640         goto loser;
641     }
642
643     eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
644     if(eKey == NULL) {
645         rv = SECFailure;
646         goto loser;
647     }
648     
649     cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
650     if (cryptoMechType == CKM_INVALID_MECHANISM) {
651         rv = SECFailure;
652         goto loser;
653     }
654
655     cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
656                                     eKey, c_param);
657     if(cx == NULL) {
658         rv = SECFailure;
659         goto loser;
660     }
661
662     rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), 
663                        (int)(src->len + 64), src->data, (int)src->len);
664     PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
665
666     bs = PK11_GetBlockSize(cryptoMechType, c_param);
667     if(bs) {
668         /* check for proper badding in block algorithms.  this assumes
669          * RC2 cbc or a DES cbc variant.  and the padding is thus defined
670          */
671         if(((int)dest->data[dest->len-1] <= bs) && 
672            ((int)dest->data[dest->len-1] > 0)) {
673             dest->len -= (int)dest->data[dest->len-1];
674         } else {
675             rv = SECFailure;
676             /* set an error ? */
677         }
678     } 
679
680 loser:
681     /* let success fall through */
682     if(result != NULL)
683         SECITEM_ZfreeItem(result, PR_TRUE);
684
685     if(rv == SECFailure)
686         PORT_ArenaRelease(poolp, mark);
687     else
688         PORT_ArenaUnmark(poolp, mark);
689
690     if(eKey != NULL)
691         PK11_FreeSymKey(eKey);
692
693     if(slot != NULL)
694         PK11_FreeSlot(slot);
695
696     if(c_param != NULL) 
697         SECITEM_ZfreeItem(c_param, PR_TRUE);
698         
699     return rv;
700 }
701
702 SECItem **
703 SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo)
704 {
705     switch(SEC_PKCS7ContentType(cinfo))
706     {
707         case SEC_OID_PKCS7_SIGNED_DATA:
708             return cinfo->content.signedData->rawCerts;
709             break;
710         default:
711             return NULL;
712             break;
713     }
714 }
715
716
717 int
718 SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo)
719 {
720   if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA)
721     return cinfo->content.envelopedData->encContentInfo.keysize;
722   else
723     return 0;
724 }
725