cmsencode.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:24k
源码类别:

CA认证

开发平台:

WINDOWS

  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.  * CMS encoding.
  35.  *
  36.  * $Id: cmsencode.c,v 1.2 2000/06/13 21:56:29 chrisk%netscape.com Exp $
  37.  */
  38. #include "cmslocal.h"
  39. #include "cert.h"
  40. #include "key.h"
  41. #include "secasn1.h"
  42. #include "secoid.h"
  43. #include "secrng.h"
  44. #include "secitem.h"
  45. #include "pk11func.h"
  46. #include "secerr.h"
  47. struct nss_cms_encoder_output {
  48.     NSSCMSContentCallback outputfn;
  49.     void *outputarg;
  50.     PLArenaPool *destpoolp;
  51.     SECItem *dest;
  52. };
  53. struct NSSCMSEncoderContextStr {
  54.     SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
  55.     PRBool ecxupdated; /* true if data was handed in */
  56.     NSSCMSMessage * cmsg; /* pointer to the root message */
  57.     SECOidTag type; /* type tag of the current content */
  58.     NSSCMSContent content; /* pointer to current content */
  59.     struct nss_cms_encoder_output output; /* output function */
  60.     int error; /* error code */
  61.     NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */
  62. };
  63. static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
  64. static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
  65. static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
  66. static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
  67.      const unsigned char *data, unsigned long len,
  68.      PRBool final, PRBool innermost);
  69. extern const SEC_ASN1Template NSSCMSMessageTemplate[];
  70. /*
  71.  * The little output function that the ASN.1 encoder calls to hand
  72.  * us bytes which we in turn hand back to our caller (via the callback
  73.  * they gave us).
  74.  */
  75. static void
  76. nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
  77.       int depth, SEC_ASN1EncodingPart data_kind)
  78. {
  79.     struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
  80.     unsigned char *dest;
  81.     unsigned long offset;
  82. #ifdef CMSDEBUG
  83.     int i;
  84.     fprintf(stderr, "kind = %d, depth = %d, len = %dn", data_kind, depth, len);
  85.     for (i=0; i < len; i++) {
  86. fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "n" : "");
  87.     }
  88.     if ((i % 16) != 0)
  89. fprintf(stderr, "n");
  90. #endif
  91.     if (output->outputfn != NULL)
  92. /* call output callback with DER data */
  93. output->outputfn(output->outputarg, buf, len);
  94.     if (output->dest != NULL) {
  95. /* store DER data in SECItem */
  96. offset = output->dest->len;
  97. if (offset == 0) {
  98.     dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
  99. } else {
  100.     dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 
  101.   output->dest->data,
  102.   output->dest->len,
  103.   output->dest->len + len);
  104. }
  105. if (dest == NULL)
  106.     /* oops */
  107.     return;
  108. output->dest->data = dest;
  109. output->dest->len += len;
  110. /* copy it in */
  111. PORT_Memcpy(output->dest->data + offset, buf, len);
  112.     }
  113. }
  114. /*
  115.  * nss_cms_encoder_notify - ASN.1 encoder callback
  116.  *
  117.  * this function is called by the ASN.1 encoder before and after the encoding of
  118.  * every object. here, it is used to keep track of data structures, set up
  119.  * encryption and/or digesting and possibly set up child encoders.
  120.  */
  121. static void
  122. nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
  123. {
  124.     NSSCMSEncoderContext *p7ecx;
  125.     NSSCMSContentInfo *rootcinfo, *cinfo;
  126.     PRBool after = !before;
  127.     PLArenaPool *poolp;
  128.     SECOidTag childtype;
  129.     SECItem *item;
  130.     p7ecx = (NSSCMSEncoderContext *)arg;
  131.     PORT_Assert(p7ecx != NULL);
  132.     rootcinfo = &(p7ecx->cmsg->contentInfo);
  133.     poolp = p7ecx->cmsg->poolp;
  134. #ifdef CMSDEBUG
  135.     fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %dn", before ? "before" : "after", dest, depth);
  136. #endif
  137.     /*
  138.      * Watch for the content field, at which point we want to instruct
  139.      * the ASN.1 encoder to start taking bytes from the buffer.
  140.      */
  141.     switch (p7ecx->type) {
  142.     default:
  143.     case SEC_OID_UNKNOWN:
  144. /* we're still in the root message */
  145. if (after && dest == &(rootcinfo->contentType)) {
  146.     /* got the content type OID now - so find out the type tag */
  147.     p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
  148.     /* set up a pointer to our current content */
  149.     p7ecx->content = rootcinfo->content;
  150. }
  151. break;
  152.     case SEC_OID_PKCS7_DATA:
  153. if (before && dest == &(rootcinfo->rawContent)) {
  154.     /* just set up encoder to grab from user - no encryption or digesting */
  155.     if ((item = rootcinfo->content.data) != NULL)
  156. (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
  157.     else
  158. SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
  159.     SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
  160. }
  161. break;
  162.     case SEC_OID_PKCS7_SIGNED_DATA:
  163.     case SEC_OID_PKCS7_ENVELOPED_DATA:
  164.     case SEC_OID_PKCS7_DIGESTED_DATA:
  165.     case SEC_OID_PKCS7_ENCRYPTED_DATA:
  166. /* when we know what the content is, we encode happily until we reach the inner content */
  167. cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
  168. childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
  169. if (after && dest == &(cinfo->contentType)) {
  170.     /* we're right before encoding the data (if we have some or not) */
  171.     /* (for encrypted data, we're right before the contentEncAlg which may change */
  172.     /*  in nss_cms_before_data because of IV calculation when setting up encryption) */
  173.     if (nss_cms_before_data(p7ecx) != SECSuccess)
  174. p7ecx->error = PORT_GetError();
  175. }
  176. if (before && dest == &(cinfo->rawContent)) {
  177.     if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
  178. /* we have data - feed it in */
  179. (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
  180.     else
  181. /* else try to get it from user */
  182. SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
  183. }
  184. if (after && dest == &(cinfo->rawContent)) {
  185.     if (nss_cms_after_data(p7ecx) != SECSuccess)
  186. p7ecx->error = PORT_GetError();
  187.     SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
  188. }
  189. break;
  190.     }
  191. }
  192. /*
  193.  * nss_cms_before_data - setup the current encoder to receive data
  194.  */
  195. static SECStatus
  196. nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
  197. {
  198.     SECStatus rv;
  199.     SECOidTag childtype;
  200.     NSSCMSContentInfo *cinfo;
  201.     PLArenaPool *poolp;
  202.     NSSCMSEncoderContext *childp7ecx;
  203.     const SEC_ASN1Template *template;
  204.     poolp = p7ecx->cmsg->poolp;
  205.     /* call _Encode_BeforeData handlers */
  206.     switch (p7ecx->type) {
  207.     case SEC_OID_PKCS7_SIGNED_DATA:
  208. /* we're encoding a signedData, so set up the digests */
  209. rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
  210. break;
  211.     case SEC_OID_PKCS7_DIGESTED_DATA:
  212. /* we're encoding a digestedData, so set up the digest */
  213. rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
  214. break;
  215.     case SEC_OID_PKCS7_ENVELOPED_DATA:
  216. rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
  217. break;
  218.     case SEC_OID_PKCS7_ENCRYPTED_DATA:
  219. rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
  220. break;
  221.     default:
  222. rv = SECFailure;
  223.     }
  224.     if (rv != SECSuccess)
  225. return SECFailure;
  226.     /* ok, now we have a pointer to cinfo */
  227.     /* find out what kind of data is encapsulated */
  228.     
  229.     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
  230.     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
  231.     switch (childtype) {
  232.     case SEC_OID_PKCS7_SIGNED_DATA:
  233.     case SEC_OID_PKCS7_ENVELOPED_DATA:
  234.     case SEC_OID_PKCS7_ENCRYPTED_DATA:
  235.     case SEC_OID_PKCS7_DIGESTED_DATA:
  236. #if 0
  237.     case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */
  238. #endif
  239. /* in these cases, we need to set up a child encoder! */
  240. /* create new encoder context */
  241. childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
  242. if (childp7ecx == NULL)
  243.     return SECFailure;
  244. /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
  245.  * (which will encrypt and/or digest it)
  246.  * this needs to route back into our update function
  247.  * which finds the lowest encoding context & encrypts and computes digests */
  248. childp7ecx->type = childtype;
  249. childp7ecx->content = cinfo->content;
  250. /* use the non-recursive update function here, of course */
  251. childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
  252. childp7ecx->output.outputarg = p7ecx;
  253. childp7ecx->output.destpoolp = NULL;
  254. childp7ecx->output.dest = NULL;
  255. childp7ecx->cmsg = p7ecx->cmsg;
  256. template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
  257. if (template == NULL)
  258.     goto loser; /* cannot happen */
  259. /* now initialize the data for encoding the first third */
  260. switch (childp7ecx->type) {
  261. case SEC_OID_PKCS7_SIGNED_DATA:
  262.     rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
  263.     break;
  264. case SEC_OID_PKCS7_ENVELOPED_DATA:
  265.     rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
  266.     break;
  267. case SEC_OID_PKCS7_DIGESTED_DATA:
  268.     rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
  269.     break;
  270. case SEC_OID_PKCS7_ENCRYPTED_DATA:
  271.     rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
  272.     break;
  273. case SEC_OID_PKCS7_DATA:
  274.     rv = SECSuccess;
  275.     break;
  276. }
  277. if (rv != SECSuccess)
  278.     goto loser;
  279. /*
  280.  * Initialize the BER encoder.
  281.  */
  282. childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
  283.    nss_cms_encoder_out, &(childp7ecx->output));
  284. if (childp7ecx->ecx == NULL)
  285.     goto loser;
  286. childp7ecx->ecxupdated = PR_FALSE;
  287. /*
  288.  * Indicate that we are streaming.  We will be streaming until we
  289.  * get past the contents bytes.
  290.  */
  291. SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
  292. /*
  293.  * The notify function will watch for the contents field.
  294.  */
  295. SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
  296. /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
  297. /* encoding process - we'll do that from the update function instead */
  298. /* otherwise we'd be encoding data from a call of the notify function of the */
  299. /* parent encoder (which would not work) */
  300. /* this will kick off the encoding process & encode everything up to the content bytes,
  301.  * at which point the notify function sets streaming mode (and possibly creates
  302.  * another child encoder). */
  303. if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
  304.     goto loser;
  305. p7ecx->childp7ecx = childp7ecx;
  306. break;
  307.     case SEC_OID_PKCS7_DATA:
  308. p7ecx->childp7ecx = NULL;
  309. break;
  310.     default:
  311. /* we do not know this type */
  312. p7ecx->error = SEC_ERROR_BAD_DER;
  313. break;
  314.     }
  315.     return SECSuccess;
  316. loser:
  317.     if (childp7ecx) {
  318. if (childp7ecx->ecx)
  319.     SEC_ASN1EncoderFinish(childp7ecx->ecx);
  320. PORT_Free(childp7ecx);
  321.     }
  322.     return SECFailure;
  323. }
  324. static SECStatus
  325. nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
  326. {
  327.     SECStatus rv;
  328.     switch (p7ecx->type) {
  329.     case SEC_OID_PKCS7_SIGNED_DATA:
  330. /* this will finish the digests and sign */
  331. rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
  332. break;
  333.     case SEC_OID_PKCS7_ENVELOPED_DATA:
  334. rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
  335. break;
  336.     case SEC_OID_PKCS7_DIGESTED_DATA:
  337. rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
  338. break;
  339.     case SEC_OID_PKCS7_ENCRYPTED_DATA:
  340. rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
  341. break;
  342.     case SEC_OID_PKCS7_DATA:
  343. /* do nothing */
  344. break;
  345.     default:
  346. rv = SECFailure;
  347. break;
  348.     }
  349.     return rv;
  350. }
  351. /*
  352.  * nss_cms_encoder_work_data - process incoming data
  353.  *
  354.  * (from the user or the next encoding layer)
  355.  * Here, we need to digest and/or encrypt, then pass it on
  356.  */
  357. static SECStatus
  358. nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
  359.      const unsigned char *data, unsigned long len,
  360.      PRBool final, PRBool innermost)
  361. {
  362.     unsigned char *buf = NULL;
  363.     SECStatus rv;
  364.     NSSCMSContentInfo *cinfo;
  365.     rv = SECSuccess; /* may as well be optimistic */
  366.     /*
  367.      * We should really have data to process, or we should be trying
  368.      * to finish/flush the last block.  (This is an overly paranoid
  369.      * check since all callers are in this file and simple inspection
  370.      * proves they do it right.  But it could find a bug in future
  371.      * modifications/development, that is why it is here.)
  372.      */
  373.     PORT_Assert ((data != NULL && len) || final);
  374.     /* we got data (either from the caller, or from a lower level encoder) */
  375.     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
  376.     /* Update the running digest. */
  377.     if (len && cinfo->digcx != NULL)
  378. NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
  379.     /* Encrypt this chunk. */
  380.     if (cinfo->ciphcx != NULL) {
  381. unsigned int inlen; /* length of data being encrypted */
  382. unsigned int outlen; /* length of encrypted data */
  383. unsigned int buflen; /* length available for encrypted data */
  384. inlen = len;
  385. buflen = NSS_CMSCipherContext_EncryptLength(cinfo->ciphcx, inlen, final);
  386. if (buflen == 0) {
  387.     /*
  388.      * No output is expected, but the input data may be buffered
  389.      * so we still have to call Encrypt.
  390.      */
  391.     rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, NULL, NULL, 0,
  392.    data, inlen, final);
  393.     if (final) {
  394. len = 0;
  395. goto done;
  396.     }
  397.     return rv;
  398. }
  399. if (dest != NULL)
  400.     buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
  401. else
  402.     buf = (unsigned char*)PORT_Alloc(buflen);
  403. if (buf == NULL) {
  404.     rv = SECFailure;
  405. } else {
  406.     rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, buf, &outlen, buflen,
  407.    data, inlen, final);
  408.     data = buf;
  409.     len = outlen;
  410. }
  411. if (rv != SECSuccess)
  412.     /* encryption or malloc failed? */
  413.     return rv;
  414.     }
  415.     /*
  416.      * at this point (data,len) has everything we'd like to give to the CURRENT encoder
  417.      * (which will encode it, then hand it back to the user or the parent encoder)
  418.      * We don't encode the data if we're innermost and we're told not to include the data
  419.      */
  420.     if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
  421. rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
  422. done:
  423.     if (cinfo->ciphcx != NULL) {
  424. if (dest != NULL) {
  425.     dest->data = buf;
  426.     dest->len = len;
  427. } else if (buf != NULL) {
  428.     PORT_Free (buf);
  429. }
  430.     }
  431.     return rv;
  432. }
  433. /*
  434.  * nss_cms_encoder_update - deliver encoded data to the next higher level
  435.  *
  436.  * no recursion here because we REALLY want to end up at the next higher encoder!
  437.  */
  438. static SECStatus
  439. nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
  440. {
  441.     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
  442.     return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
  443. }
  444. /*
  445.  * NSS_CMSEncoder_Start - set up encoding of a CMS message
  446.  *
  447.  * "cmsg" - message to encode
  448.  * "outputfn", "outputarg" - callback function for delivery of DER-encoded output
  449.  *                           will not be called if NULL.
  450.  * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
  451.  * "destpoolp" - pool to allocate DER-encoded output in
  452.  * "pwfn", pwfn_arg" - callback function for getting token password
  453.  * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
  454.  * "detached_digestalgs", "detached_digests" - digests from detached content
  455.  */
  456. NSSCMSEncoderContext *
  457. NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
  458. NSSCMSContentCallback outputfn, void *outputarg,
  459. SECItem *dest, PLArenaPool *destpoolp,
  460. PK11PasswordFunc pwfn, void *pwfn_arg,
  461. NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
  462. SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
  463. {
  464.     NSSCMSEncoderContext *p7ecx;
  465.     SECStatus rv;
  466.     NSSCMSContentInfo *cinfo;
  467.     NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
  468. detached_digestalgs, detached_digests);
  469.     p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
  470.     if (p7ecx == NULL) {
  471. PORT_SetError(SEC_ERROR_NO_MEMORY);
  472. return NULL;
  473.     }
  474.     p7ecx->cmsg = cmsg;
  475.     p7ecx->output.outputfn = outputfn;
  476.     p7ecx->output.outputarg = outputarg;
  477.     p7ecx->output.dest = dest;
  478.     p7ecx->output.destpoolp = destpoolp;
  479.     p7ecx->type = SEC_OID_UNKNOWN;
  480.     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
  481.     switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
  482.     case SEC_OID_PKCS7_SIGNED_DATA:
  483. rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
  484. break;
  485.     case SEC_OID_PKCS7_ENVELOPED_DATA:
  486. rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
  487. break;
  488.     case SEC_OID_PKCS7_DIGESTED_DATA:
  489. rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
  490. break;
  491.     case SEC_OID_PKCS7_ENCRYPTED_DATA:
  492. rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
  493. break;
  494.     default:
  495. rv = SECFailure;
  496. break;
  497.     }
  498.     if (rv != SECSuccess)
  499. return NULL;
  500.     /* Initialize the BER encoder.
  501.      * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
  502.     p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
  503.        nss_cms_encoder_out, &(p7ecx->output));
  504.     if (p7ecx->ecx == NULL) {
  505. PORT_Free (p7ecx);
  506. return NULL;
  507.     }
  508.     p7ecx->ecxupdated = PR_FALSE;
  509.     /*
  510.      * Indicate that we are streaming.  We will be streaming until we
  511.      * get past the contents bytes.
  512.      */
  513.     SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
  514.     /*
  515.      * The notify function will watch for the contents field.
  516.      */
  517.     SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
  518.     /* this will kick off the encoding process & encode everything up to the content bytes,
  519.      * at which point the notify function sets streaming mode (and possibly creates
  520.      * a child encoder). */
  521.     if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
  522. PORT_Free (p7ecx);
  523. return NULL;
  524.     }
  525.     return p7ecx;
  526. }
  527. /*
  528.  * NSS_CMSEncoder_Update - take content data delivery from the user
  529.  *
  530.  * "p7ecx" - encoder context
  531.  * "data" - content data
  532.  * "len" - length of content data
  533.  *
  534.  * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
  535.  * then hand the data to the work_data fn
  536.  */
  537. SECStatus
  538. NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
  539. {
  540.     SECStatus rv;
  541.     NSSCMSContentInfo *cinfo;
  542.     SECOidTag childtype;
  543.     if (p7ecx->error)
  544. return SECFailure;
  545.     /* hand data to the innermost decoder */
  546.     if (p7ecx->childp7ecx) {
  547. /* recursion here */
  548. rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
  549.     } else {
  550. /* we are at innermost decoder */
  551. /* find out about our inner content type - must be data */
  552. cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
  553. childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
  554. if (childtype != SEC_OID_PKCS7_DATA)
  555.     return SECFailure;
  556. /* and we must not have preset data */
  557. if (cinfo->content.data != NULL)
  558.     return SECFailure;
  559. /*  hand it the data so it can encode it (let DER trickle up the chain) */
  560. rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
  561.     }
  562.     return rv;
  563. }
  564. /*
  565.  * NSS_CMSEncoder_Cancel - stop all encoding
  566.  *
  567.  * we need to walk down the chain of encoders and the finish them from the innermost out
  568.  */
  569. SECStatus
  570. NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
  571. {
  572.     SECStatus rv = SECFailure;
  573.     /* XXX do this right! */
  574.     /*
  575.      * Finish any inner decoders before us so that all the encoded data is flushed
  576.      * This basically finishes all the decoders from the innermost to the outermost.
  577.      * Finishing an inner decoder may result in data being updated to the outer decoder
  578.      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
  579.      */
  580.     if (p7ecx->childp7ecx) {
  581. rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
  582. /* remember rv for now */
  583.     }
  584.     /*
  585.      * On the way back up, there will be no more data (if we had an
  586.      * inner encoder, it is done now!)
  587.      * Flush out any remaining data and/or finish digests.
  588.      */
  589.     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
  590.     if (rv != SECSuccess)
  591. goto loser;
  592.     p7ecx->childp7ecx = NULL;
  593.     /* kick the encoder back into working mode again.
  594.      * We turn off streaming stuff (which will cause the encoder to continue
  595.      * encoding happily, now that we have all the data (like digests) ready for it).
  596.      */
  597.     SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
  598.     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
  599.     /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
  600.     rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
  601. loser:
  602.     SEC_ASN1EncoderFinish(p7ecx->ecx);
  603.     PORT_Free (p7ecx);
  604.     return rv;
  605. }
  606. /*
  607.  * NSS_CMSEncoder_Finish - signal the end of data
  608.  *
  609.  * we need to walk down the chain of encoders and the finish them from the innermost out
  610.  */
  611. SECStatus
  612. NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
  613. {
  614.     SECStatus rv = SECFailure;
  615.     NSSCMSContentInfo *cinfo;
  616.     SECOidTag childtype;
  617.     /*
  618.      * Finish any inner decoders before us so that all the encoded data is flushed
  619.      * This basically finishes all the decoders from the innermost to the outermost.
  620.      * Finishing an inner decoder may result in data being updated to the outer decoder
  621.      * while we are already in NSS_CMSEncoder_Finish, but that's allright.
  622.      */
  623.     if (p7ecx->childp7ecx) {
  624. rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
  625. if (rv != SECSuccess)
  626.     goto loser;
  627.     }
  628.     /*
  629.      * On the way back up, there will be no more data (if we had an
  630.      * inner encoder, it is done now!)
  631.      * Flush out any remaining data and/or finish digests.
  632.      */
  633.     rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
  634.     if (rv != SECSuccess)
  635. goto loser;
  636.     p7ecx->childp7ecx = NULL;
  637.     /* find out about our inner content type - must be data */
  638.     cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
  639.     childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
  640.     if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
  641. SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
  642. /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
  643. rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
  644.     }
  645.     SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
  646.     if (p7ecx->error)
  647. rv = SECFailure;
  648. loser:
  649.     SEC_ASN1EncoderFinish(p7ecx->ecx);
  650.     PORT_Free (p7ecx);
  651.     return rv;
  652. }