/ libsecurity_smime / lib / cmscinfo.c
cmscinfo.c
  1  /*
  2   * The contents of this file are subject to the Mozilla Public
  3   * License Version 1.1 (the "License"); you may not use this file
  4   * except in compliance with the License. You may obtain a copy of
  5   * the License at http://www.mozilla.org/MPL/
  6   * 
  7   * Software distributed under the License is distributed on an "AS
  8   * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9   * implied. See the License for the specific language governing
 10   * rights and limitations under the License.
 11   * 
 12   * The Original Code is the Netscape security libraries.
 13   * 
 14   * The Initial Developer of the Original Code is Netscape
 15   * Communications Corporation.  Portions created by Netscape are 
 16   * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
 17   * Rights Reserved.
 18   * 
 19   * Contributor(s):
 20   * 
 21   * Alternatively, the contents of this file may be used under the
 22   * terms of the GNU General Public License Version 2 or later (the
 23   * "GPL"), in which case the provisions of the GPL are applicable 
 24   * instead of those above.  If you wish to allow use of your 
 25   * version of this file only under the terms of the GPL and not to
 26   * allow others to use your version of this file under the MPL,
 27   * indicate your decision by deleting the provisions above and
 28   * replace them with the notice and other provisions required by
 29   * the GPL.  If you do not delete the provisions above, a recipient
 30   * may use your version of this file under either the MPL or the
 31   * GPL.
 32   */
 33  
 34  /*
 35   * CMS contentInfo methods.
 36   */
 37  
 38  #include <Security/SecCmsContentInfo.h>
 39  
 40  #include <Security/SecCmsDigestContext.h>
 41  #include <Security/SecCmsDigestedData.h>
 42  #include <Security/SecCmsEncryptedData.h>
 43  #include <Security/SecCmsEnvelopedData.h>
 44  #include <Security/SecCmsSignedData.h>
 45  
 46  #include "cmslocal.h"
 47  
 48  //#include "pk11func.h"
 49  #include "secoid.h"
 50  #include "SecAsn1Item.h"
 51  
 52  #include <security_asn1/secerr.h>
 53  #include <security_asn1/secport.h>
 54  
 55  #include <Security/SecBase.h>
 56  
 57  /*
 58   * SecCmsContentInfoDestroy - destroy a CMS contentInfo and all of its sub-pieces.
 59   */
 60  void
 61  SecCmsContentInfoDestroy(SecCmsContentInfoRef cinfo)
 62  {
 63      SECOidTag kind;
 64  
 65      if(!cinfo) return;
 66  
 67      kind = SecCmsContentInfoGetContentTypeTag(cinfo);
 68      switch (kind) {
 69      case SEC_OID_PKCS7_ENVELOPED_DATA:
 70  	SecCmsEnvelopedDataDestroy(cinfo->content.envelopedData);
 71  	break;
 72        case SEC_OID_PKCS7_SIGNED_DATA:
 73  	SecCmsSignedDataDestroy(cinfo->content.signedData);
 74  	break;
 75        case SEC_OID_PKCS7_ENCRYPTED_DATA:
 76  	SecCmsEncryptedDataDestroy(cinfo->content.encryptedData);
 77  	break;
 78        case SEC_OID_PKCS7_DIGESTED_DATA:
 79  	SecCmsDigestedDataDestroy(cinfo->content.digestedData);
 80  	break;
 81        default:
 82  	/* XXX Anything else that needs to be "manually" freed/destroyed? */
 83  	break;
 84      }
 85      if (cinfo->digcx) {
 86  	/* must destroy digest objects */
 87  	SecCmsDigestContextCancel(cinfo->digcx);
 88  	cinfo->digcx = NULL;
 89      }
 90      if (cinfo->bulkkey)
 91  	CFRelease(cinfo->bulkkey);
 92      /* @@@ private key is only here as a workaround for 3401088.  Note this *must* be released after bulkkey */
 93      if (cinfo->privkey)
 94  	CFRelease(cinfo->privkey);
 95  
 96      if (cinfo->ciphcx) {
 97  	SecCmsCipherContextDestroy(cinfo->ciphcx);
 98  	cinfo->ciphcx = NULL;
 99      }
100      
101      /* we live in a pool, so no need to worry about storage */
102  }
103  
104  /*
105   * SecCmsContentInfoGetChildContentInfo - get content's contentInfo (if it exists)
106   */
107  SecCmsContentInfoRef
108  SecCmsContentInfoGetChildContentInfo(SecCmsContentInfoRef cinfo)
109  {
110      void *ptr = NULL;
111      SecCmsContentInfoRef ccinfo = NULL;
112      SECOidTag tag = SecCmsContentInfoGetContentTypeTag(cinfo);
113      switch (tag) {
114      case SEC_OID_PKCS7_SIGNED_DATA:
115  	ptr = (void *)cinfo->content.signedData;
116  	ccinfo = &(cinfo->content.signedData->contentInfo);
117  	break;
118      case SEC_OID_PKCS7_ENVELOPED_DATA:
119  	ptr = (void *)cinfo->content.envelopedData;
120  	ccinfo = &(cinfo->content.envelopedData->contentInfo);
121  	break;
122      case SEC_OID_PKCS7_DIGESTED_DATA:
123  	ptr = (void *)cinfo->content.digestedData;
124  	ccinfo = &(cinfo->content.digestedData->contentInfo);
125  	break;
126      case SEC_OID_PKCS7_ENCRYPTED_DATA:
127  	ptr = (void *)cinfo->content.encryptedData;
128  	ccinfo = &(cinfo->content.encryptedData->contentInfo);
129  	break;
130      case SEC_OID_PKCS7_DATA:
131      default:
132  	break;
133      }
134      return (ptr ? ccinfo : NULL);
135  }
136  
137  /*
138   * SecCmsContentInfoSetContent - set content type & content
139   */
140  OSStatus
141  SecCmsContentInfoSetContent(SecCmsContentInfoRef cinfo, SECOidTag type, void *ptr)
142  {
143      OSStatus rv;
144  
145      cinfo->contentTypeTag = SECOID_FindOIDByTag(type);
146      if (cinfo->contentTypeTag == NULL)
147  	return errSecParam;
148      
149      /* do not copy the oid, just create a reference */
150      rv = SECITEM_CopyItem (cinfo->cmsg->poolp, &(cinfo->contentType), &(cinfo->contentTypeTag->oid));
151      if (rv != SECSuccess)
152  	return errSecAllocate;
153  
154      cinfo->content.pointer = ptr;
155  
156      if (type != SEC_OID_PKCS7_DATA) {
157  	/* as we always have some inner data,
158  	 * we need to set it to something, just to fool the encoder enough to work on it
159  	 * and get us into nss_cms_encoder_notify at that point */
160  	cinfo->rawContent = SECITEM_AllocItem(cinfo->cmsg->poolp, NULL, 1);
161  	if (cinfo->rawContent == NULL) {
162  	    PORT_SetError(SEC_ERROR_NO_MEMORY);
163  	    return errSecAllocate;
164  	}
165      }
166  
167      return errSecSuccess;
168  }
169  
170  /*
171   * SecCmsContentInfoSetContentXXXX - typesafe wrappers for SecCmsContentInfoSetContent
172   */
173  
174  /*
175   * data == NULL -> pass in data via SecCmsEncoderUpdate
176   * data != NULL -> take this data
177   */
178  OSStatus
179  SecCmsContentInfoSetContentData(SecCmsContentInfoRef cinfo, CFDataRef dataRef, Boolean detached)
180  {
181      SecAsn1Item * data = NULL;
182      if (dataRef) {
183  	/* @@@ Fixme CFRetain the passed in data rather than
184  	   always copying it for performance. */
185  	data = PORT_ArenaAlloc(cinfo->cmsg->poolp, sizeof(SecAsn1Item));
186  	data->Length = CFDataGetLength(dataRef);
187  	if (data->Length) {
188  	    data->Data = PORT_ArenaAlloc(cinfo->cmsg->poolp, data->Length);
189  	    memcpy(data->Data, CFDataGetBytePtr(dataRef), data->Length);
190  	}
191  	else
192  	    data->Data = NULL;
193      }
194  
195      if (SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_DATA, (void *)data) != SECSuccess) {
196          OSStatus status = PORT_GetError();
197          PORT_SetError(0); // clean the thread since we've returned this error
198          return status;
199      }
200      cinfo->rawContent = (detached) ? 
201  			    NULL : (data) ? 
202  				data : SECITEM_AllocItem(cinfo->cmsg->poolp, NULL, 1);
203      return errSecSuccess;
204  }
205  
206  OSStatus
207  SecCmsContentInfoSetContentSignedData(SecCmsContentInfoRef cinfo, SecCmsSignedDataRef sigd)
208  {
209      return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_SIGNED_DATA, (void *)sigd);
210  }
211  
212  OSStatus
213  SecCmsContentInfoSetContentEnvelopedData(SecCmsContentInfoRef cinfo, SecCmsEnvelopedDataRef envd)
214  {
215      return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_ENVELOPED_DATA, (void *)envd);
216  }
217  
218  OSStatus
219  SecCmsContentInfoSetContentDigestedData(SecCmsContentInfoRef cinfo, SecCmsDigestedDataRef digd)
220  {
221      return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_DIGESTED_DATA, (void *)digd);
222  }
223  
224  OSStatus
225  SecCmsContentInfoSetContentEncryptedData(SecCmsContentInfoRef cinfo, SecCmsEncryptedDataRef encd)
226  {
227      return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_ENCRYPTED_DATA, (void *)encd);
228  }
229  
230  OSStatus
231  SecCmsContentInfoSetContentOther(SecCmsContentInfoRef cinfo, SecAsn1Item *data, Boolean detached, const SecAsn1Oid *eContentType)
232  {
233      SECStatus srtn;
234      SECOidData *tmpOidData;
235  
236      /* just like SecCmsContentInfoSetContentData, except override the contentType and
237       * contentTypeTag. This OID is for encoding... */
238      srtn = SECITEM_CopyItem (cinfo->cmsg->poolp, &(cinfo->contentType), eContentType);
239      if (srtn != SECSuccess) {
240          return errSecAllocate;
241      }
242  
243      /* this serves up a contentTypeTag with an empty OID */
244      tmpOidData = SECOID_FindOIDByTag(SEC_OID_OTHER);
245      /* but that's const: cook up a new one we can write to */
246      cinfo->contentTypeTag = (SECOidData *)PORT_ArenaZAlloc(cinfo->cmsg->poolp, sizeof(SECOidData));
247      *cinfo->contentTypeTag = *tmpOidData;
248      /* now fill in the OID */
249      srtn = SECITEM_CopyItem (cinfo->cmsg->poolp, &(cinfo->contentTypeTag->oid), eContentType);
250      if (srtn != SECSuccess) {
251          return errSecAllocate;
252      }
253      cinfo->content.pointer = data;
254      cinfo->rawContent = (detached) ? NULL : (data) ?
255                                  data : SECITEM_AllocItem(cinfo->cmsg->poolp, NULL, 1);
256      return noErr;
257  }
258  
259  /*
260   * SecCmsContentInfoGetContent - get pointer to inner content
261   *
262   * needs to be casted...
263   */
264  void *
265  SecCmsContentInfoGetContent(SecCmsContentInfoRef cinfo)
266  {
267      SECOidTag tag = (cinfo && cinfo->contentTypeTag) 
268  		     ? cinfo->contentTypeTag->offset 
269  		     : SEC_OID_UNKNOWN;
270      switch (tag) {
271      case SEC_OID_PKCS7_DATA:
272      case SEC_OID_PKCS7_SIGNED_DATA:
273      case SEC_OID_PKCS7_ENVELOPED_DATA:
274      case SEC_OID_PKCS7_DIGESTED_DATA:
275      case SEC_OID_PKCS7_ENCRYPTED_DATA:
276  	return cinfo->content.pointer;
277      default:
278  	return NULL;
279      }
280  }
281  
282  /* 
283   * SecCmsContentInfoGetInnerContent - get pointer to innermost content
284   *
285   * this is typically only called by SecCmsMessageGetContent()
286   */
287  const SecAsn1Item *
288  SecCmsContentInfoGetInnerContent(SecCmsContentInfoRef cinfo)
289  {
290      SecCmsContentInfoRef ccinfo;
291      SECOidTag tag;
292      SecAsn1Item * pItem;
293  
294      tag = SecCmsContentInfoGetContentTypeTag(cinfo);
295      switch (tag) {
296      case SEC_OID_PKCS7_DATA:
297  	/* end of recursion - every message has to have a data cinfo */
298  	pItem = cinfo->content.data; 
299  	break;
300      case SEC_OID_PKCS7_DIGESTED_DATA:
301      case SEC_OID_PKCS7_ENCRYPTED_DATA:
302      case SEC_OID_PKCS7_ENVELOPED_DATA:
303      case SEC_OID_PKCS7_SIGNED_DATA:
304  	ccinfo = SecCmsContentInfoGetChildContentInfo(cinfo);
305  	if (ccinfo == NULL)
306  	    pItem = NULL;
307  	else
308  	    pItem = SecCmsContentInfoGetContent(ccinfo);
309  	break;
310      default:
311  	PORT_Assert(0);
312  	pItem = NULL;
313  	break;
314      }
315      return pItem;
316  }
317  
318  /*
319   * SecCmsContentInfoGetContentType{Tag,OID} - find out (saving pointer to lookup result
320   * for future reference) and return the inner content type.
321   */
322  SECOidTag
323  SecCmsContentInfoGetContentTypeTag(SecCmsContentInfoRef cinfo)
324  {
325      if (cinfo->contentTypeTag == NULL)
326  	cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
327  
328      if (cinfo->contentTypeTag == NULL)
329  	return SEC_OID_UNKNOWN;
330  
331      return cinfo->contentTypeTag->offset;
332  }
333  
334  SecAsn1Oid *
335  SecCmsContentInfoGetContentTypeOID(SecCmsContentInfoRef cinfo)
336  {
337      if (cinfo->contentTypeTag == NULL)
338  	cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
339  
340      if (cinfo->contentTypeTag == NULL)
341  	return NULL;
342  
343      return &(cinfo->contentTypeTag->oid);
344  }
345  
346  /*
347   * SecCmsContentInfoGetContentEncAlgTag - find out (saving pointer to lookup result
348   * for future reference) and return the content encryption algorithm tag.
349   */
350  SECOidTag
351  SecCmsContentInfoGetContentEncAlgTag(SecCmsContentInfoRef cinfo)
352  {
353      if (cinfo->contentEncAlgTag == SEC_OID_UNKNOWN)
354  	cinfo->contentEncAlgTag = SECOID_GetAlgorithmTag(&(cinfo->contentEncAlg));
355  
356      return cinfo->contentEncAlgTag;
357  }
358  
359  /*
360   * SecCmsContentInfoGetContentEncAlg - find out and return the content encryption algorithm tag.
361   */
362  SECAlgorithmID *
363  SecCmsContentInfoGetContentEncAlg(SecCmsContentInfoRef cinfo)
364  {
365      return &(cinfo->contentEncAlg);
366  }
367  
368  OSStatus
369  SecCmsContentInfoSetContentEncAlg(SecCmsContentInfoRef cinfo,
370  				  SECOidTag bulkalgtag, const SecAsn1Item *parameters, int keysize)
371  {
372      PLArenaPool *poolp = cinfo->cmsg->poolp;
373      OSStatus rv;
374  
375      rv = SECOID_SetAlgorithmID(poolp, &(cinfo->contentEncAlg), bulkalgtag, parameters);
376      if (rv != SECSuccess)
377  	return SECFailure;
378      cinfo->keysize = keysize;
379      return SECSuccess;
380  }
381  
382  OSStatus
383  SecCmsContentInfoSetContentEncAlgID(SecCmsContentInfoRef cinfo,
384  				    SECAlgorithmID *algid, int keysize)
385  {
386      PLArenaPool *poolp = cinfo->cmsg->poolp;
387      OSStatus rv;
388  
389      rv = SECOID_CopyAlgorithmID(poolp, &(cinfo->contentEncAlg), algid);
390      if (rv != SECSuccess)
391  	return SECFailure;
392      if (keysize >= 0)
393  	cinfo->keysize = keysize;
394      return SECSuccess;
395  }
396  
397  void
398  SecCmsContentInfoSetBulkKey(SecCmsContentInfoRef cinfo, SecSymmetricKeyRef bulkkey)
399  {
400  #ifdef USE_CDSA_CRYPTO
401      const CSSM_KEY *cssmKey = NULL;
402  #endif
403      if (!bulkkey || !cinfo) return;
404      cinfo->bulkkey = bulkkey;
405      CFRetain(cinfo->bulkkey);
406  #ifdef USE_CDSA_CRYPTO
407      SecKeyGetCSSMKey(cinfo->bulkkey, &cssmKey);
408      cinfo->keysize = cssmKey ? cssmKey->KeyHeader.LogicalKeySizeInBits : 0;
409  #else
410      long long bulkKeySize = CFDataGetLength((CFDataRef)bulkkey) * 8;
411      if (bulkKeySize < INT_MAX) {
412          cinfo->keysize = (int)bulkKeySize;
413      } else {
414          CFRelease(cinfo->bulkkey);
415          cinfo->bulkkey = NULL;
416          cinfo->keysize = 0;
417      }
418  #endif
419  }
420  
421  SecSymmetricKeyRef
422  SecCmsContentInfoGetBulkKey(SecCmsContentInfoRef cinfo)
423  {
424      if (cinfo->bulkkey == NULL)
425  	return NULL;
426  
427      CFRetain(cinfo->bulkkey);
428      return cinfo->bulkkey;
429  }
430  
431  int
432  SecCmsContentInfoGetBulkKeySize(SecCmsContentInfoRef cinfo)
433  {
434      return cinfo->keysize;
435  }