/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* * CMS encoding. */ #include #include #include #include #include "cmslocal.h" #include "secoid.h" #include "secitem.h" #include #include #include struct nss_cms_encoder_output { SecCmsContentCallback outputfn; void *outputarg; PLArenaPool *destpoolp; CSSM_DATA_PTR dest; }; struct SecCmsEncoderStr { SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */ Boolean ecxupdated; /* true if data was handed in */ SecCmsMessageRef cmsg; /* pointer to the root message */ SECOidTag type; /* type tag of the current content */ SecCmsContent content; /* pointer to current content */ struct nss_cms_encoder_output output; /* output function */ int error; /* error code */ SecCmsEncoderRef childp7ecx; /* link to child encoder context */ }; static OSStatus nss_cms_before_data(SecCmsEncoderRef p7ecx); static OSStatus nss_cms_after_data(SecCmsEncoderRef p7ecx); static OSStatus nss_cms_encoder_update(SecCmsEncoderRef p7ecx, const char *data, size_t len); static OSStatus nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, CSSM_DATA_PTR dest, const unsigned char *data, size_t len, Boolean final, Boolean innermost); extern const SecAsn1Template SecCmsMessageTemplate[]; /* * The little output function that the ASN.1 encoder calls to hand * us bytes which we in turn hand back to our caller (via the callback * they gave us). */ static void nss_cms_encoder_out(void *arg, const char *buf, size_t len, int depth, SEC_ASN1EncodingPart data_kind) { struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; unsigned char *dest; CSSM_SIZE offset; #ifdef CMSDEBUG int i; fprintf(stderr, "kind = %d, depth = %d, len = %lu\n", data_kind, depth, len); for (i=0; i < len; i++) { fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); } if ((i % 16) != 0) fprintf(stderr, "\n"); #endif if (output->outputfn != NULL) /* call output callback with DER data */ output->outputfn(output->outputarg, buf, len); if (output->dest != NULL) { /* store DER data in CSSM_DATA */ offset = output->dest->Length; if (offset == 0) { dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len); } else { dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, output->dest->Data, output->dest->Length, output->dest->Length + len); } if (dest == NULL) /* oops */ return; output->dest->Data = dest; output->dest->Length += len; /* copy it in */ PORT_Memcpy(output->dest->Data + offset, buf, len); } } /* * nss_cms_encoder_notify - ASN.1 encoder callback * * this function is called by the ASN.1 encoder before and after the encoding of * every object. here, it is used to keep track of data structures, set up * encryption and/or digesting and possibly set up child encoders. */ static void nss_cms_encoder_notify(void *arg, Boolean before, void *dest, int depth) { SecCmsEncoderRef p7ecx; SecCmsContentInfoRef rootcinfo, cinfo; Boolean after = !before; PLArenaPool *poolp; SECOidTag childtype; CSSM_DATA_PTR item; p7ecx = (SecCmsEncoderRef)arg; PORT_Assert(p7ecx != NULL); rootcinfo = &(p7ecx->cmsg->contentInfo); poolp = p7ecx->cmsg->poolp; #ifdef CMSDEBUG fprintf(stderr, "%6.6s, dest = %p, depth = %d\n", before ? "before" : "after", dest, depth); #endif /* * Watch for the content field, at which point we want to instruct * the ASN.1 encoder to start taking bytes from the buffer. */ switch (p7ecx->type) { default: case SEC_OID_UNKNOWN: /* we're still in the root message */ if (after && dest == &(rootcinfo->contentType)) { /* got the content type OID now - so find out the type tag */ p7ecx->type = SecCmsContentInfoGetContentTypeTag(rootcinfo); /* set up a pointer to our current content */ p7ecx->content = rootcinfo->content; } break; case SEC_OID_PKCS7_DATA: case SEC_OID_OTHER: if (before && dest == &(rootcinfo->rawContent)) { /* just set up encoder to grab from user - no encryption or digesting */ if ((item = rootcinfo->content.data) != NULL) (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE); else SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ } break; case SEC_OID_PKCS7_SIGNED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: /* when we know what the content is, we encode happily until we reach the inner content */ cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); childtype = SecCmsContentInfoGetContentTypeTag(cinfo); if (after && dest == &(cinfo->contentType)) { /* we're right before encoding the data (if we have some or not) */ /* (for encrypted data, we're right before the contentEncAlg which may change */ /* in nss_cms_before_data because of IV calculation when setting up encryption) */ if (nss_cms_before_data(p7ecx) != SECSuccess) p7ecx->error = PORT_GetError(); } if (before && dest == &(cinfo->rawContent)) { if ( ((childtype == SEC_OID_PKCS7_DATA) || (childtype == SEC_OID_OTHER)) && ((item = cinfo->content.data) != NULL)) /* we have data - feed it in */ (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE); else /* else try to get it from user */ SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); } if (after && dest == &(cinfo->rawContent)) { if (nss_cms_after_data(p7ecx) != SECSuccess) p7ecx->error = PORT_GetError(); SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ } break; } } /* * nss_cms_before_data - setup the current encoder to receive data */ static OSStatus nss_cms_before_data(SecCmsEncoderRef p7ecx) { OSStatus rv; SECOidTag childtype; SecCmsContentInfoRef cinfo; PLArenaPool *poolp; SecCmsEncoderRef childp7ecx; const SecAsn1Template *template; poolp = p7ecx->cmsg->poolp; /* call _Encode_BeforeData handlers */ switch (p7ecx->type) { case SEC_OID_PKCS7_SIGNED_DATA: /* we're encoding a signedData, so set up the digests */ rv = SecCmsSignedDataEncodeBeforeData(p7ecx->content.signedData); break; case SEC_OID_PKCS7_DIGESTED_DATA: /* we're encoding a digestedData, so set up the digest */ rv = SecCmsDigestedDataEncodeBeforeData(p7ecx->content.digestedData); break; case SEC_OID_PKCS7_ENVELOPED_DATA: rv = SecCmsEnvelopedDataEncodeBeforeData(p7ecx->content.envelopedData); break; case SEC_OID_PKCS7_ENCRYPTED_DATA: rv = SecCmsEncryptedDataEncodeBeforeData(p7ecx->content.encryptedData); break; default: rv = SECFailure; } if (rv != SECSuccess) return SECFailure; /* ok, now we have a pointer to cinfo */ /* find out what kind of data is encapsulated */ cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); childtype = SecCmsContentInfoGetContentTypeTag(cinfo); switch (childtype) { case SEC_OID_PKCS7_SIGNED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: #if 0 case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */ #endif /* in these cases, we need to set up a child encoder! */ /* create new encoder context */ childp7ecx = PORT_ZAlloc(sizeof(struct SecCmsEncoderStr)); if (childp7ecx == NULL) return SECFailure; /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder * (which will encrypt and/or digest it) * this needs to route back into our update function * which finds the lowest encoding context & encrypts and computes digests */ childp7ecx->type = childtype; childp7ecx->content = cinfo->content; /* use the non-recursive update function here, of course */ childp7ecx->output.outputfn = (SecCmsContentCallback)nss_cms_encoder_update; childp7ecx->output.outputarg = p7ecx; childp7ecx->output.destpoolp = NULL; childp7ecx->output.dest = NULL; childp7ecx->cmsg = p7ecx->cmsg; template = SecCmsUtilGetTemplateByTypeTag(childtype); if (template == NULL) goto loser; /* cannot happen */ /* now initialize the data for encoding the first third */ switch (childp7ecx->type) { case SEC_OID_PKCS7_SIGNED_DATA: rv = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData); break; case SEC_OID_PKCS7_ENVELOPED_DATA: rv = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData); break; case SEC_OID_PKCS7_DIGESTED_DATA: rv = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData); break; case SEC_OID_PKCS7_ENCRYPTED_DATA: rv = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData); break; case SEC_OID_PKCS7_DATA: case SEC_OID_OTHER: rv = SECSuccess; break; default: PORT_Assert(0); break; } if (rv != SECSuccess) goto loser; /* * Initialize the BER encoder. */ childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, nss_cms_encoder_out, &(childp7ecx->output)); if (childp7ecx->ecx == NULL) goto loser; childp7ecx->ecxupdated = PR_FALSE; /* * Indicate that we are streaming. We will be streaming until we * get past the contents bytes. */ SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); /* * The notify function will watch for the contents field. */ SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx); /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ /* encoding process - we'll do that from the update function instead */ /* otherwise we'd be encoding data from a call of the notify function of the */ /* parent encoder (which would not work) */ /* this will kick off the encoding process & encode everything up to the content bytes, * at which point the notify function sets streaming mode (and possibly creates * another child encoder). */ if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess) goto loser; p7ecx->childp7ecx = childp7ecx; break; case SEC_OID_PKCS7_DATA: case SEC_OID_OTHER: p7ecx->childp7ecx = NULL; break; default: /* we do not know this type */ p7ecx->error = SEC_ERROR_BAD_DER; break; } return SECSuccess; loser: if (childp7ecx) { if (childp7ecx->ecx) SEC_ASN1EncoderFinish(childp7ecx->ecx); PORT_Free(childp7ecx); } return SECFailure; } static OSStatus nss_cms_after_data(SecCmsEncoderRef p7ecx) { OSStatus rv = SECFailure; switch (p7ecx->type) { case SEC_OID_PKCS7_SIGNED_DATA: /* this will finish the digests and sign */ rv = SecCmsSignedDataEncodeAfterData(p7ecx->content.signedData); break; case SEC_OID_PKCS7_ENVELOPED_DATA: rv = SecCmsEnvelopedDataEncodeAfterData(p7ecx->content.envelopedData); break; case SEC_OID_PKCS7_DIGESTED_DATA: rv = SecCmsDigestedDataEncodeAfterData(p7ecx->content.digestedData); break; case SEC_OID_PKCS7_ENCRYPTED_DATA: rv = SecCmsEncryptedDataEncodeAfterData(p7ecx->content.encryptedData); break; case SEC_OID_PKCS7_DATA: case SEC_OID_OTHER: /* do nothing */ break; default: rv = SECFailure; break; } return rv; } /* * nss_cms_encoder_work_data - process incoming data * * (from the user or the next encoding layer) * Here, we need to digest and/or encrypt, then pass it on */ static OSStatus nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, CSSM_DATA_PTR dest, const unsigned char *data, size_t len, Boolean final, Boolean innermost) { unsigned char *buf = NULL; OSStatus rv; SecCmsContentInfoRef cinfo; rv = SECSuccess; /* may as well be optimistic */ /* * We should really have data to process, or we should be trying * to finish/flush the last block. (This is an overly paranoid * check since all callers are in this file and simple inspection * proves they do it right. But it could find a bug in future * modifications/development, that is why it is here.) */ PORT_Assert ((data != NULL && len) || final); /* we got data (either from the caller, or from a lower level encoder) */ cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); /* Update the running digest. */ if (len && cinfo->digcx != NULL) SecCmsDigestContextUpdate(cinfo->digcx, data, len); /* Encrypt this chunk. */ if (cinfo->ciphcx != NULL) { CSSM_SIZE inlen; /* length of data being encrypted */ CSSM_SIZE outlen; /* length of encrypted data */ CSSM_SIZE buflen; /* length available for encrypted data */ inlen = len; buflen = SecCmsCipherContextEncryptLength(cinfo->ciphcx, inlen, final); if (buflen == 0) { /* * No output is expected, but the input data may be buffered * so we still have to call Encrypt. */ rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, NULL, NULL, 0, data, inlen, final); if (final) { len = 0; goto done; } return rv; } if (dest != NULL) buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); else buf = (unsigned char*)PORT_Alloc(buflen); if (buf == NULL) { rv = SECFailure; } else { rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, buf, &outlen, buflen, data, inlen, final); data = buf; len = outlen; } if (rv != SECSuccess) /* encryption or malloc failed? */ return rv; } /* * at this point (data,len) has everything we'd like to give to the CURRENT encoder * (which will encode it, then hand it back to the user or the parent encoder) * We don't encode the data if we're innermost and we're told not to include the data */ if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL)) rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); done: if (cinfo->ciphcx != NULL) { if (dest != NULL) { dest->Data = buf; dest->Length = len; } else if (buf != NULL) { PORT_Free (buf); } } return rv; } /* * nss_cms_encoder_update - deliver encoded data to the next higher level * * no recursion here because we REALLY want to end up at the next higher encoder! */ static OSStatus nss_cms_encoder_update(SecCmsEncoderRef p7ecx, const char *data, size_t len) { /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE); } /* * SecCmsEncoderCreate - set up encoding of a CMS message * * "cmsg" - message to encode * "outputfn", "outputarg" - callback function for delivery of DER-encoded output * will not be called if NULL. * "dest" - if non-NULL, pointer to CSSM_DATA that will hold the DER-encoded output * "destpoolp" - pool to allocate DER-encoded output in * "pwfn", pwfn_arg" - callback function for getting token password * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData * "detached_digestalgs", "detached_digests" - digests from detached content */ OSStatus SecCmsEncoderCreate(SecCmsMessageRef cmsg, SecCmsContentCallback outputfn, void *outputarg, CSSM_DATA_PTR dest, SecArenaPoolRef destpool, PK11PasswordFunc pwfn, void *pwfn_arg, SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, SECAlgorithmID **detached_digestalgs, CSSM_DATA_PTR *detached_digests, SecCmsEncoderRef *outEncoder) { SecCmsEncoderRef p7ecx; OSStatus result; SecCmsContentInfoRef cinfo; SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, detached_digestalgs, detached_digests); p7ecx = (SecCmsEncoderRef)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr)); if (p7ecx == NULL) { result = memFullErr; goto loser; } p7ecx->cmsg = cmsg; p7ecx->output.outputfn = outputfn; p7ecx->output.outputarg = outputarg; p7ecx->output.dest = dest; p7ecx->output.destpoolp = (PLArenaPool *)destpool; p7ecx->type = SEC_OID_UNKNOWN; cinfo = SecCmsMessageGetContentInfo(cmsg); switch (SecCmsContentInfoGetContentTypeTag(cinfo)) { case SEC_OID_PKCS7_SIGNED_DATA: result = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData); break; case SEC_OID_PKCS7_ENVELOPED_DATA: result = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData); break; case SEC_OID_PKCS7_DIGESTED_DATA: result = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData); break; case SEC_OID_PKCS7_ENCRYPTED_DATA: result = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData); break; default: /* @@@ We need a better error for unsupported message types. */ result = paramErr; break; } if (result) goto loser; /* Initialize the BER encoder. * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, SecCmsMessageTemplate, nss_cms_encoder_out, &(p7ecx->output)); if (p7ecx->ecx == NULL) { result = PORT_GetError(); PORT_Free (p7ecx); goto loser; } p7ecx->ecxupdated = PR_FALSE; /* * Indicate that we are streaming. We will be streaming until we * get past the contents bytes. */ SEC_ASN1EncoderSetStreaming(p7ecx->ecx); /* * The notify function will watch for the contents field. */ SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); /* this will kick off the encoding process & encode everything up to the content bytes, * at which point the notify function sets streaming mode (and possibly creates * a child encoder). */ if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { result = PORT_GetError(); PORT_Free (p7ecx); goto loser; } *outEncoder = p7ecx; loser: return result; } /* * SecCmsEncoderUpdate - take content data delivery from the user * * "p7ecx" - encoder context * "data" - content data * "len" - length of content data * * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), * then hand the data to the work_data fn */ OSStatus SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx, const void *data, CFIndex len) { OSStatus result; SecCmsContentInfoRef cinfo; SECOidTag childtype; if (p7ecx->error) return p7ecx->error; /* hand data to the innermost decoder */ if (p7ecx->childp7ecx) { /* recursion here */ result = SecCmsEncoderUpdate(p7ecx->childp7ecx, data, len); } else { /* we are at innermost decoder */ /* find out about our inner content type - must be data */ cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); childtype = SecCmsContentInfoGetContentTypeTag(cinfo); if ((childtype != SEC_OID_PKCS7_DATA) && (childtype != SEC_OID_OTHER)) return paramErr; /* @@@ Maybe come up with a better error? */ /* and we must not have preset data */ if (cinfo->content.data != NULL) return paramErr; /* @@@ Maybe come up with a better error? */ /* hand it the data so it can encode it (let DER trickle up the chain) */ result = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE); if (result) result = PORT_GetError(); } return result; } /* * SecCmsEncoderDestroy - stop all encoding * * we need to walk down the chain of encoders and the finish them from the innermost out */ void SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx) { /* XXX do this right! */ /* * Finish any inner decoders before us so that all the encoded data is flushed * This basically finishes all the decoders from the innermost to the outermost. * Finishing an inner decoder may result in data being updated to the outer decoder * while we are already in SecCmsEncoderFinish, but that's allright. */ if (p7ecx->childp7ecx) SecCmsEncoderDestroy(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ /* * On the way back up, there will be no more data (if we had an * inner encoder, it is done now!) * Flush out any remaining data and/or finish digests. */ if (nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL))) goto loser; p7ecx->childp7ecx = NULL; /* kick the encoder back into working mode again. * We turn off streaming stuff (which will cause the encoder to continue * encoding happily, now that we have all the data (like digests) ready for it). */ SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); SEC_ASN1EncoderClearStreaming(p7ecx->ecx); /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); loser: SEC_ASN1EncoderFinish(p7ecx->ecx); PORT_Free (p7ecx); } /* * SecCmsEncoderFinish - signal the end of data * * we need to walk down the chain of encoders and the finish them from the innermost out */ OSStatus SecCmsEncoderFinish(SecCmsEncoderRef p7ecx) { OSStatus result; SecCmsContentInfoRef cinfo; SECOidTag childtype; /* * Finish any inner decoders before us so that all the encoded data is flushed * This basically finishes all the decoders from the innermost to the outermost. * Finishing an inner decoder may result in data being updated to the outer decoder * while we are already in SecCmsEncoderFinish, but that's allright. */ if (p7ecx->childp7ecx) { result = SecCmsEncoderFinish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ if (result) goto loser; } /* * On the way back up, there will be no more data (if we had an * inner encoder, it is done now!) * Flush out any remaining data and/or finish digests. */ result = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); if (result) { result = PORT_GetError(); goto loser; } p7ecx->childp7ecx = NULL; /* find out about our inner content type - must be data */ cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); childtype = SecCmsContentInfoGetContentTypeTag(cinfo); if ( ((childtype == SEC_OID_PKCS7_DATA) || (childtype == SEC_OID_OTHER)) && (cinfo->content.data == NULL)) { SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ result = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); if (result) result = PORT_GetError(); } SEC_ASN1EncoderClearStreaming(p7ecx->ecx); if (p7ecx->error && !result) result = p7ecx->error; loser: SEC_ASN1EncoderFinish(p7ecx->ecx); PORT_Free (p7ecx); return result; } OSStatus SecCmsMessageEncode(SecCmsMessageRef cmsg, const CSSM_DATA *input, SecArenaPoolRef arena, CSSM_DATA_PTR outBer) { SecCmsEncoderRef encoder; OSStatus result; if (!cmsg || !outBer || !arena) { result = paramErr; goto loser; } result = SecCmsEncoderCreate(cmsg, 0, 0, outBer, arena, 0, 0, 0, 0, 0, 0, &encoder); if (result) goto loser; if (input) { result = SecCmsEncoderUpdate(encoder, input->Data, input->Length); if (result) { SecCmsEncoderDestroy(encoder); goto loser; } } result = SecCmsEncoderFinish(encoder); loser: return result; }